@metalift/mcp
Advanced tools
| 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 }; | ||
| } |
| export {}; |
| 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<{ |
+3
-0
@@ -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}`); |
+60
-2
@@ -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" |
+10
-0
@@ -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 | |
35563
15.77%17
30.77%706
14.61%77
14.93%