🎩 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.6
to
1.0.7
+12
dist/web-search.d.ts
export declare const WEB_SEARCH_RESULT_LIMIT = 10;
export declare const WEB_SEARCH_CREDITS = 2;
export declare function buildWebSearchRequest(args: {
query: string;
categories?: string[];
language?: string;
}): {
limit: number;
query: string;
categories?: string[];
language?: string;
};
export const WEB_SEARCH_RESULT_LIMIT = 10;
export const WEB_SEARCH_CREDITS = 2;
export function buildWebSearchRequest(args) {
return { ...args, limit: WEB_SEARCH_RESULT_LIMIT };
}
import assert from "node:assert/strict";
import test from "node:test";
import { buildWebSearchRequest, WEB_SEARCH_RESULT_LIMIT } from "./web-search.js";
test("buildWebSearchRequest always uses top-10 limit", () => {
assert.equal(WEB_SEARCH_RESULT_LIMIT, 10);
assert.deepEqual(buildWebSearchRequest({ query: "docker docs" }), {
query: "docker docs",
limit: 10,
});
assert.deepEqual(buildWebSearchRequest({ query: "news", categories: ["news"], language: "en" }), { query: "news", categories: ["news"], language: "en", limit: 10 });
});
+1
-0

@@ -22,2 +22,3 @@ import type { ScrapeArgs } from "./scrape-args.js";

map(params: Record<string, unknown>): Promise<Record<string, unknown> & BillingMeta>;
search(params: Record<string, unknown>): Promise<Record<string, unknown> & BillingMeta>;
jobStatus(jobId: string): Promise<Record<string, unknown> & BillingMeta>;

@@ -24,0 +25,0 @@ listStrategies(): Promise<{

@@ -99,2 +99,5 @@ const B2B_ATTESTATION_HEADER = "X-B2B-Attestation";

}
search(params) {
return this.request("/v1/search", { method: "POST", body: JSON.stringify(params) });
}
jobStatus(jobId) {

@@ -101,0 +104,0 @@ return this.request(`/v1/jobs/${jobId}`);

@@ -6,12 +6,28 @@ #!/usr/bin/env node

import { MetaliftClient } from "./client.js";
import { buildWebSearchRequest, WEB_SEARCH_RESULT_LIMIT } from "./web-search.js";
import { normalizeScrapeArgs } from "./scrape-args.js";
const COMPLIANCE_NOTICE = "You are solely responsible for complying with website terms, robots.txt, copyright, and data protection laws when using scraped content.";
const SERVER_INSTRUCTIONS = `Metalift provides web search and web scraping as separate, independently billed tools.
Web search (metalift_web_search):
- Returns SERP metadata only: title, url, snippet, engine, score — not full page content.
- Costs 2 credits per successful search (flat fee).
- Always returns up to ${WEB_SEARCH_RESULT_LIMIT} results.
- Do NOT auto-scrape all search results. Review snippets first, then call metalift_scrape only for URLs that need full content.
Scraping (metalift_scrape, metalift_batch_scrape):
- Fetches page content (markdown, html, text). Billed per URL (static=1, JS=5, premium=10+ credits).
- Use after search when the user or agent needs full page content from specific URLs.
Recommended agent workflow: metalift_web_search → pick 0–N relevant URLs from snippets → metalift_scrape chosen URLs only.`;
const client = new MetaliftClient();
const server = new McpServer({
name: "metalift",
version: "1.0.0",
version: "1.0.7",
}, {
instructions: SERVER_INSTRUCTIONS,
});
server.registerTool("metalift_scrape", {
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 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}`,
description: `Scrape a single URL into markdown, HTML, or text for LLM context. Separate from web search — use after metalift_web_search when full page content is needed. 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: {

@@ -97,2 +113,17 @@ url: z.string().url(),

});
server.registerTool("metalift_web_search", {
title: "Web Search",
description: `Search the web and return up to ${WEB_SEARCH_RESULT_LIMIT} SERP results (title, url, snippet, engine, score). Costs 2 credits per search. Returns search snippets only — not page content. Do not auto-scrape results; pick relevant URLs and call metalift_scrape separately for full content.`,
inputSchema: {
query: z.string().min(1).max(512),
categories: z.array(z.string()).optional(),
language: z.string().max(16).optional(),
},
annotations: { readOnlyHint: true },
}, async (args) => {
const result = await client.search(buildWebSearchRequest(args));
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
});
server.registerTool("metalift_job_status", {

@@ -168,2 +199,3 @@ title: "Get Job Status",

"metalift_map",
"metalift_web_search",
"metalift_job_status",

@@ -174,2 +206,7 @@ "metalift_list_strategies",

],
web_search: {
credits_per_search: 2,
result_limit: WEB_SEARCH_RESULT_LIMIT,
decoupled_from_scrape: true,
},
}, null, 2),

@@ -248,2 +285,23 @@ },

}));
server.registerPrompt("research_topic", {
title: "Research Topic",
description: "Search the web for a topic, then scrape only the most relevant URLs (decoupled search + scrape workflow)",
argsSchema: {
query: z.string().describe("Search query"),
},
}, async ({ query }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `Research "${query}" using this decoupled workflow:
1. Call metalift_web_search with query "${query}" (2 credits, top 10 SERP snippets).
2. Review titles and snippets — do NOT scrape every result.
3. Call metalift_scrape only for the 1–3 URLs that need full page content.
4. Synthesize an answer citing sources.`,
},
},
],
}));
async function main() {

@@ -250,0 +308,0 @@ const transport = new StdioServerTransport();

+2
-2
{
"name": "@metalift/mcp",
"mcpName": "io.github.MetaLift-AI/metalift",
"version": "1.0.6",
"version": "1.0.7",
"description": "Metalift MCP server for AI agents",

@@ -16,3 +16,3 @@ "license": "SEE LICENSE IN LICENSE",

"dev": "tsx src/index.ts",
"test": "npm run build && node --test dist/client.test.js dist/scrape-args.test.js",
"test": "npm run build && node --test dist/client.test.js dist/scrape-args.test.js dist/web-search.test.js",
"prepublishOnly": "npm run build && node ../../scripts/npm-prepare-publish.mjs strip",

@@ -19,0 +19,0 @@ "postpublish": "node ../../scripts/npm-prepare-publish.mjs restore"

@@ -47,2 +47,11 @@ # @metalift/mcp

## Web search vs scrape
Search and scrape are **separate tools** with separate billing:
1. **`metalift_web_search`** — returns up to 10 search snippets (2 credits). Review titles and snippets first.
2. **`metalift_scrape`** — fetches full page content for URLs you choose (1+ credits per URL).
Do not auto-scrape every search result. See [Web search](../../packages/platform-web/docs/web-search.md) for agent workflow examples.
## Environment variables

@@ -63,2 +72,3 @@

| `metalift_map` | Discover site URLs |
| `metalift_web_search` | Web search — top 10 SERP results (title, url, snippet). **2 credits** per search. Does not fetch page content; use `metalift_scrape` separately for URLs you need |
| `metalift_job_status` | Poll async jobs |

@@ -65,0 +75,0 @@ | `metalift_list_strategies` | List scrape strategies |