mcpField
M
MeshArc

MeshArc

远程服务
mesharc.dev浏览器自动化04 天前更新

关于 MeshArc

MeshArc 是通过 API 抓取、爬取和搜索网页的 Python 客户端,面向需要提取页面内容或跟踪网站变化的开发者。它还能将一次性爬取转为定期监控项目,并记录页面增删改动。

主要功能

  1. 01抓取页面并输出 Markdown、文本等内容
  2. 02批量抓取多个 URL
  3. 03按站点地图映射 URL
  4. 04搜索网页并抓取结果页面
  5. 05创建定期爬取项目并记录变化

适用场景

  1. 01提取网页正文或结构化字段
  2. 02批量读取多个页面
  3. 03检查站点地图中的页面
  4. 04追踪网站页面的新增、删除和修改

MCP 相关

MeshArc 提供 MCP 工具,供 MCP 客户端调用其网页抓取、爬取、映射、搜索及 Agent 能力;README 提到部分功能尚未在 mesharc.dev 上线。

配置

远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。

服务地址https://mcp.mesharc.dev/mcp
一键复制安装命令
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。

工具

详细介绍

mesharc

MeshArc API 的 Python 客户端:MeshArc:输入 URL,输出干净内容,并记录发生的变更。

  • 抓取单个页面或一批页面——获取 Markdown、文本、HTML、链接、结构化字段和截图。
  • 爬取整个网站,无需预先设置项目;如果发现值得持续关注,也可以将其设为项目。
  • 映射网站在站点地图中声明的内容,之后再抓取。
  • 搜索网页,找到 URL 未知的页面,并在同一次调用中读取每个结果页面。
  • 提问Agent:它会搜索、读取页面并作答;如果你提供架构,它会按该架构输出答案,并附上答案所依据的页面。(mesharc.dev 上尚未启用。)
  • 监控搜索或问题:每小时、每天或每周重新查询,将每次答案与上次比较,并在答案发生变化时通过 webhook 通知。(mesharc.dev 上尚未启用。)
  • 监视网站随时间的变化:项目、定时运行和变更记录——逐字段记录新增、移除和修改的页面。

支持 Python 3.10 或更高版本。只有一个依赖项(httpx)。类型标注完整。

安装

pip install mesharc

身份验证

每次调用都需要 API 密钥。在应用的 Settings → API keys 中创建密钥——它只会显示一次——然后将其交给客户端,或者将其放入 MESHARC_API_KEY,并在构造客户端时不传入参数:

from mesharc import MeshArc

arc = MeshArc("mesharc_...")
# or, with MESHARC_API_KEY in the environment:
arc = MeshArc()
# options: MeshArc(api_key, timeout=150.0, max_retries=2)

密钥只会作为 bearer 请求头发送到 api.mesharc.dev。

密钥包含创建时设置的作用域(read、write、admin),还可以包含允许其访问的项目集合、过期时间和速率限制。密钥无权使用的路由会返回 403;密钥无权访问的项目会返回 404。

快速开始

from mesharc import MeshArc

arc = MeshArc("mesharc_...")

page = arc.scrape("https://example.com/pricing")
print(page["markdown"])
print(page["verdict"], page["method"], page["credits"])   # ok crawler 1

scrape 会一直保持请求打开,直到页面返回(默认 60 s),因此抓取普通页面时无需轮询。

阅读页面

单个页面
page = arc.scrape("https://quotes.toscrape.com/js/", config={"render_js": "always"})

config 可以是项目接受的任意设置,使用其 API 名称(render_js、only_main_content、formats、max_tier、wait_for_selector、actions 等)。完整列表及默认值请参见 mesharc.dev/docs/configuration。

page = arc.scrape_one(
    "https://example.com/",
    formats="markdown,text,cleanHtml",     # which bodies to return
    timeout_s=120,                         # how long the API holds the request (120 max)
    idempotency_key="pricing-2026-09-18",  # the same key returns the first answer for 24 h
)
多个页面

URL 列表会作为批次处理:按主机分组,根据配置允许的情况并行抓取,并为每个 URL 返回一行。

batch = arc.scrape(["https://a.com/", "https://b.com/x"], config={"concurrency": 4})
for row in batch["pages"]:
    print(row["url"], row["httpStatus"], row["verdict"], row["credits"])

arc.scrape(urls, wait=False) 会立即返回批次 ID;arc.batch(id, wait=True) 会在之后完成该批次。传入 webhook_url=,即可通过通知获知结果,而无需轮询(batch.finished,使用一次性返回的密钥签名)。

页面是什么样的

每一行页面数据都包含相同的字段,无论它来自抓取、爬取还是项目:

字段含义
markdown、text、cleanHtml、html、links、fields、screenshot你请求的内容
httpStatus网站返回的状态
verdictok、thin(页面较短,但确实是页面)、blocked(拒绝访问或返回 404)、skipped
errorCodeOK,或出错原因:BLOCKED、NOT_FOUND、TIER_LIMIT、CAPTCHA、LOGIN_REQUIRED、RATE_LIMITED、TIMEOUT 等
shape、warnings、signals对于标记能说明内容类型的短页面,可能是 listing / table / form;short;以及判定结果的原因
method、tier、climbedTo读取页面的引擎(crawler、tls、minted、browser、browser-residential 等)、层级,以及被拒绝的页面经过了多少层级
credits、billedAs费用;如果计费层级与实际抓取层级不同,则显示计费所用层级
words、language、head、reason、crawledAt大小、语言、head 字段、判定说明和抓取时间

网站拒绝访问的页面费用为 0,404 页面也一样。

抓取网站

job = arc.crawl(
    "https://docs.example.com",
    limit=200,                          # page budget
    maxDepth=3,                         # link hops from the seed
    includePaths=["/docs/*"],
    crawlMode="sitemap_first",          # what the sitemap declares first, then links
    maxTier="browser",                  # how far a refused page may climb
    scrapeOptions={"formats": ["markdown", "links"]},
    config={"crawl_delay_ms": 500},     # any project setting, directly
)

for page in job.pages():                # follows the cursor while the crawl runs
    print(page["url"], page["words"])
print(job.status, job.envelope["counts"], job.envelope["creditsUsed"])

crawl 会立即返回一个句柄;job.pages() 会在页面到达时逐个返回,并在抓取结束时停止。wait=True 会阻塞直到抓取完成;job.wait()、job.refresh()、job.cancel() 的作用顾名思义;arc.get_crawl(id) 可重新连接到从其他地方启动的抓取。

一次性抓取会在 30 天后过期。如果某个网站值得持续监控:

project = job.keep(name="Docs", schedule="weekly")

选项中的 webhook=({"url", "events", "metadata", "include"})会收到 crawl.started、crawl.page(每条消息包含 50 个页面)和 crawl.completed 事件;签名密钥只会返回一次,作为 job.webhook_secret。设置 "include": ["markdown"] 后,crawl.page 消息也会携带每个页面的 Markdown,单条消息最多约 1 MB:如果页面内容超出限制或未被存储,则会以 "markdown": null 返回,并附带 markdownOmitted(too_large 或 not_stored)以及 contentUrl。contentUrl 是一个使用你的密钥读取的 API 路径(job.page(url) 也能读取相同内容)。这需要在事件中包含 crawl.page;如果省略 events,默认就会包含该事件。

映射网站

for u in arc.map("https://docs.example.com"):
    print(u["url"], u["lastmod"])

details = arc.map_details("https://www.gov.uk/", search="visa", limit=500)
print(details["totals"], details["creditsUsed"])   # {'files': 29, 'urls': 508431, …} 29

读取每个站点地图文件需要消耗 1 个 credit;大多数网站只有一个文件。

搜索网络

out = arc.web_search(
    "python packaging guide",
    limit=5,                            # 1 to 10 results
    freshness="month",                  # hour | day | week | month | year
    include_domains=["python.org"],     # exclude_domains= too; an exclude wins
    scrape=True,                        # each result's page as markdown as well
)

if out["status"] == "blocked":          # every engine refused; nothing was charged
    print(out["attempts"])
else:
    for hit in out["data"]:
        print(hit["position"], hit["title"], hit["url"], hit["snippet"])
        if hit.get("page"):
            print(hit["page"].get("markdown", "")[:200])
print(out["engine"], out["cached"], out["creditsUsed"])

again = arc.get_search(out["id"])       # a search started earlier, by id
for s in arc.searches(q="python"):      # the workspace's searches, newest first
    print(s["id"], s["query"], s["status"], s["resultCount"], s["creditsUsed"])

news = arc.web_search("python release", news=True, freshness="day")   # the engines' news results
for hit in news["data"]:
    print(hit["publisher"], hit["age"], hit["title"])                  # who published it, and how long ago ('20h')
more = arc.web_search("python packaging guide", page=2)               # results 11-20

web_search 会等待结果,并返回完整的响应对象,搜索结果位于 data 中。搜索状态为 queued、running、done、blocked 或 error:blocked 表示所有搜索引擎都拒绝了请求,会直接返回,不会抛出错误;error 会抛出 MeshArcError。如果搜索会抓取页面,则会一直处于 running 状态,直到页面到达。scrape= 也接受字典,并会按原样发送({"formats": ["markdown", "links"], "maxCredits": 20});country= 和 lang= 指定搜索的地区和语言;wait=False 会立即返回;timeout_s 指定 API 保持请求的时长(默认 60 秒,最长 120 秒),之后客户端会轮询。

news=True 会搜索各引擎的新闻结果,而非网络搜索结果(每次搜索只能二选一);每条新闻结果还会带有 publisher 和 age,这些值由搜索引擎提供(如 "20h")。page= 指定结果页,范围为 1 到 10:第 2 页包含第 11–20 条结果,每一页都是独立搜索,并单独计费。news= 和 page= 需要使用支持这些功能的 API:它们位于 API 的功能分支中,目前尚未发布到 mesharc.dev。

搜索需要可写入的密钥,并会消耗 credits。结果页按读取该页面的搜索引擎收费;如果所有搜索引擎都拒绝提供结果页,则不收费。在一次搜索完成后的 1 小时内(新闻搜索为 10 分钟),若再次执行相同搜索(query、news、page、country、lang、freshness 和域名均相同),结果会从缓存中返回(cached 为 true),且结果页不收费。无论是否命中缓存,抓取的页面都会收费。

arc.search(project_id, q) 是另一种搜索:它会搜索项目已存储的页面,而非网络(见下文)。

向 Agent 提问

mesharc.dev 上的 Agent 路由(/agent)目前尚未上线;网页搜索已上线。客户端和 MCP 工具已准备就绪。Agent 的 webhook、connection_id、continue_() 和 field_sources 也需要 API 支持这些功能:它们目前位于 API 的功能分支上,尚未部署到 mesharc.dev。

Agent 会接收问题、自行搜索网页、梳理网站并阅读页面,然后根据所用页面给出回答。提供一个 schema,回答就会按该结构返回:

run = arc.agent(
    "What does each plan on example.com cost per month?",
    urls=["https://example.com/pricing"],   # pages to start from, up to 20; omit to let it search
    allowed_domains=["example.com"],        # keep it to these sites, up to 20
    schema={                                # a JSON Schema of type object or array
        "type": "object",
        "properties": {"plans": {"type": "array", "items": {"type": "object", "properties": {
            "name": {"type": "string"}, "monthly": {"type": "number"}}}}},
    },
    max_credits=500,                        # the most the run may spend: 1 to 100,000, 2,000 by default
    max_steps=20,                           # the most steps it may take: 1 to 100, 40 by default
)

out = run.wait()                            # blocks until it finishes; the envelope
print(out["status"], out["creditsUsed"], out["steps"])
print(run.data)                             # {'plans': [...]}, matching the schema
for src in run.sources:                     # the pages the answer rests on
    print(src["url"], src["title"])
print(run.field_sources)                    # {'plans[0].monthly': {'url': ..., 'pageId': ...}, ...}

agent 会立即返回一个 AgentRun,其状态为已排队或正在运行;提示词最多可有 10,000 个字符。timeout_s= 会要求 API 为回答暂缓返回请求(最多 120 秒)。如果没有 schema,回答会是 {"text": ...}。field_sources 会说明回答中每个值的来源:它的路径("plans[0].monthly",或列表回答中的 "[2].name")对应到 {url, pageId},即本次运行读取过的页面。

run = arc.agent(
    "What does each plan on example.com cost per month?",
    webhook={"url": "https://hooks.example.com/mesharc", "events": ["agent.completed", "agent.failed"]},
    connection_id="conn_...",               # run on one of the workspace's own LLM connections
)
print(run.webhook_secret)                   # once: the secret the webhook's messages are signed with

webhook= 可以是一个 URL,也可以是 {"url", "events", "metadata},用于接收 agent.started、agent.action、agent.completed、agent.failed 和 agent.cancelled 通知(默认包含全部五种事件);其签名密钥只会返回一次,位于 run.webhook_secret 中;continue_() 返回的句柄会沿用已停止运行的密钥(新运行会保留同一 webhook),而通过 get_agent 重新打开的运行不会包含密钥。connection_id= 会让 Agent 使用工作区自己的某个 LLM 连接运行——使用该连接的模型、密钥,并由该连接计费。

要查看运行过程,请跟踪事件:

run = arc.agent("Who maintains the httpx library, and where is it developed?")
for event in run.trace():                   # follows the run until it ends
    print(event["seq"], event["kind"], event["text"])
print(run.wait()["data"])                   # {'text': '...'}

kind 可以是 start、resume、continue、model、search、fetch、render、map、select、extract、lookup(数据提供方的记录)、propose、tool、busy 或 finish。trace(after=seq) 会从已查看过的事件之后继续;follow=False 会返回现有事件,然后结束。

run.cancel()                                # {'id': ..., 'status': 'cancelled'} if it was queued, 'cancelling' if running
run.refresh()                               # the run as it stands now

again = arc.get_agent(run.id)               # a run started earlier, by id
for r in arc.agent_runs(status="done"):     # the workspace's runs, newest first
    print(r["id"], r["status"], r["creditsUsed"], r["prompt"])
from datetime import date
for r in arc.agent_runs(model="openai:gpt-5.4-mini", since="2026-10-01", until=date.today()):
    print(r["id"], r["model"], r["createdAt"])

运行状态可以是 queued、running、done、error、cancelled 或 credit_limit。wait() 会返回状态为 done、cancelled 或因达到 max_credits 而停止在 credit_limit 的运行——已找到的内容位于 data["partial"] 中;如果状态为 error,则会抛出 MeshArcError 502 job_failed,并附带 API 的错误文本;对于其他未知的结束状态,行为与 Node 客户端相同。取消操作会立即停止已排队的运行,并在正在运行的任务进入下一步之前将其停止;已使用的额度仍会扣除。无需句柄,也可以通过运行 ID 调用 arc.cancel_agent(id) 执行相同操作。

agent_runs 可按 status、按运行所用模型名称筛选,也可按运行创建时间筛选:时间在 since 或之后,以及在 until 之前;两者都可以是 ISO 8601 日期或日期时间字符串,也可以是 date / datetime。日期表示 UTC 午夜;不带时区的日期时间会被视为 UTC(因此,朴素的 datetime.now() 会被当作 UTC 时间读取你的本地时间——请改用 datetime.now(timezone.utc))。

因达到 credit_limit 而停止的运行可以使用新的额度继续:

out = run.wait()
if out["status"] == "credit_limit":
    more = run.continue_(max_credits=1_000)   # a new run, on the same thread
    print(more.continues_run_id)              # run.id
    print(more.wait()["data"])                # the full answer, replacing the partial one

continue_() 会返回新运行的 AgentRun,从已停止运行中断的位置继续,保留已读取的页面(不会再次收费)及其 webhook;新句柄的 webhook_secret 会继续携带该 webhook 的密钥。max_credits 和 max_steps 属于新运行自己的设置;如果省略,则沿用已停止运行的设置。刷新后,已停止运行的 continued_by 会记录新运行。若运行并非因达到额度上限而停止、已继续过,或没有保存的进度,则会抛出代码为 conflict 的 MeshArcError 409;若已超过保留期限,则会抛出 410 expired。

运行需要具有写入权限的密钥,并会消耗额度:读取的每个页面都会按一次读取计费(被拒绝访问的页面免费);模型 token 则按模型提供商的价格加收 20% 计费——如果使用工作区自己的连接,则每 1,000 个 token 收取 1 个额度;所有费用都计入 max_credits。运行会保留 7 天;之后,get_agent 会抛出状态为 410、代码为 expired 的 MeshArcError,而 trace() 会静默结束。

后续跟进与对话

**尚未在 mesharc.dev 上线:**本节中的所有功能都需要 API 支持,这些功能目前位于 API 的功能分支上。

first = arc.agent("Which plans does example.com sell?", urls=["https://example.com/pricing"])
first.wait()
nxt = first.follow_up("And which of them include SSO?")   # the same conversation
print(nxt.wait()["data"])

for c in arc.conversations(q="example.com"):              # the latest message first
    print(c["id"], c["messages"], c["title"])
thread = arc.conversation(first.id)                        # every run in it, oldest first, under "runs"

后续跟进会回答此前对话中提出和发现的所有内容,并且可以免费重新读取对话读取过的任何页面。它的请求独立于对话本身——follow_up 接受与 agent 相同的参数——并为自身的工作付费。只有对话的最新消息可以在处理完成后跟进一次;否则会返回 MeshArcError 409 conflict(错误中会指出最新消息),或者在超过 30 条消息后无法跟进。

已保存的登录信息、数据提供方和提案

**尚未在 mesharc.dev 上线:**本节中的所有功能都需要 API 支持,这些功能目前位于 API 的功能分支上。

run = arc.agent(
    "How many employees does Stripe have, and what was Apple's revenue last year?",
    data_connections=["conn_pdl", "conn_fmp"],   # the workspace's data providers, one per provider
    logins="auto",                               # the saved logins it may sign in with (the default)
    proposals=True,                              # it may propose things to watch
)

data_connections= 最多指定工作区的两个数据提供方连接(见下文的 arc.connections):People Data Labs 用于公司数据,Financial Modeling Prep 用于金融数据。此后,Agent 可以使用你自己的密钥查询数据——费用由提供方收取,MeshArc 不收取额度——每条找到的记录都会成为 run.sources 中的一项,格式为 {url, title, provider},其中 url 是记录来源的请求(不会包含你的密钥)。Agent 会以未登录状态读取页面;若某个页面需要账户,则会使用工作区为该页面所属网站保存的登录信息(arc.logins),逐页登录。logins= 用于指定它可以使用哪些登录信息(默认使用全部,也可以指定 "none" 或 ID 列表);login_id= 是用于指定单个 ID 列表的旧版形式。登录信息需按部署单独启用。proposals=True 允许 Agent 提议抓取某个网站,或按计划保存搜索或问题;只有成员批准后才会创建(arc.proposals)。

监控器:按计划重复执行的搜索或提问

监控器(arc.monitors,/monitors 路由)需要 API 提供相应功能:该功能目前位于 API 的功能分支中,尚未上线到 mesharc.dev。因此,下文介绍其上限、提醒、记录和语气。

monitor = arc.monitors.create(
    "search",                                         # or "agent"
    {"query": "mesharc", "freshness": "day"},         # a POST /search or POST /agent body, in the API's own names
    "daily",                                          # hourly | daily | weekly
    name="Mentions",
    webhook="https://hooks.example.com/mesharc",      # told search.changed / agent.changed
)
print(monitor["webhookSecret"])                       # once

for r in arc.monitors.runs(monitor["id"]):            # newest first
    print(r["createdAt"], r["status"], r["changed"], r["summary"])

press = arc.monitors.create("search", {"query": "mesharc", "sources": ["news"]}, "hourly")   # a news monitor
arc.monitors.update(monitor["id"], schedule="weekly")  # only what is not None is sent
arc.monitors.update(monitor["id"], webhook="")         # the empty string removes the webhook

每次运行都是一次常规搜索或 Agent 运行,按一次计费,并与上一次已返回结果的运行进行比较:搜索会比较新增、移除和位置变动的结果;Agent 运行会比较答案中新增、更改和移除的值。运行的 refId 是它所创建的搜索或 Agent 运行(arc.get_search / arc.get_agent)。

request 是 API 中 POST /search 或 POST /agent 请求体使用的字段名称(驼峰命名),例如 {"query": ..., "includeDomains": [...]}、{"prompt": ..., "schema": ..., "maxCredits": ...},而不是 web_search 或 agent 的 Python 参数。因此,新闻监控器会指定 "sources": ["news"]:web_search(news=True) 会将其参数映射为该值,而 monitors.create 不会。搜索监控器仅读取结果页面,因此不接受 scrape。baseline_id= 接受一个已完成的搜索或 Agent 运行,且其请求必须与首次运行相同;该基线不会再次计费(对于搜索,基线的 limit 必须大于或等于监控器的值);若不指定,则从现在开始首次运行。

方法功能
monitors.create(kind, request, schedule, name=None, webhook=None, baseline_id=None, idempotency_key=None, budget=None, alerts=None, add_to_project=None, rows=None, destination=None, sentiment=None)按计划持续运行搜索或 Agent 请求
monitors.list(kind=None) · monitors.get(id)工作区的监控器列表,每个监控器都包含 lastRun;或单个监控器
monitors.overview(kind=None)所有按计划运行的项目和监控器,包括下次和上次运行时间、本月支出、估算值和上限
monitors.update(id, name=None, schedule=None, status=None, webhook=None, budget=, alerts=, add_to_project=, rows=, destination=, sentiment=)更改非 None 的字段;status 可设为 active 或 paused,webhook="" 会移除 webhook
monitors.pause(id) · monitors.resume(id)停止或重新启动计划;暂停期间错过的时段会在恢复时运行一次
monitors.run(id, idempotency_key=None)立即运行;运行期间会返回 409 conflict
monitors.runs(id, limit=25)查看其运行记录,包含 changed、summary 和 diff,并可通过 next 获取后续记录
monitors.delete(id)删除监控器及其运行记录;它创建的搜索和 Agent 运行会保留
monitors.rows(id, active=None, q=None) · monitors.row(id, key) · monitors.row_events(id, run_id=None, kind=None) · monitors.export_rows(id, path, dataset="rows", fmt="csv")查看 Agent 监控器的记录、单条记录及其历史、变动记录,并将全部内容导出为文件
plans = arc.monitors.create(
    "agent", {"prompt": "List example.com's plans with their monthly price", "urls": ["https://example.com/pricing"],
              "allowedDomains": ["example.com"]}, "weekly",
    budget={"perRun": 300, "perMonth": 1500},          # credits; 0 or left out is no cap
    alerts={"email": True, "chatUrl": "https://hooks.slack.com/services/..."},
    rows={"path": "plans", "key": "name"},             # follow the answer's list, plan by plan
)
for row in arc.monitors.rows(plans["id"]):
    print(row["key"], row["attrs"], row["sources"])   # its columns, and the page each was read on
for event in arc.monitors.row_events(plans["id"]):    # added, changed (before and after), gone
    print(event["kind"], event["key"])

budget 限制每次运行的额度(perRun 会降低 Agent 监控器的 maxCredits)和每月额度(超过 perMonth 时会跳过到期运行,且 webhook 每月会收到一次 budget.reached)。当运行发现内容时,alerts 会发送工作区运行邮件和聊天消息(Slack、Discord、Teams 或 Google Chat 的传入 webhook)。搜索监控器的 add_to_project 会将该项目网站上的新增结果添加到项目中;Agent 监控器的 rows 会逐条跟踪列表答案中的记录,而 destination 会在每次运行后写出这些记录。新闻监控器的 sentiment={"connectionId": ..., "only": ["negative"]} 会使用工作区的某个大模型连接为每次运行中的新增结果评分(每 1,000 个 token 收取 1 个额度),并且只针对这些语气发送聊天和邮件。update 会整体替换 budget、alerts 和 sentiment(设为 {} 表示无);add_to_project="" 和 destination="" 会移除对应设置。

创建和运行监控器会消耗额度。除 monitors.list、monitors.get 和 monitors.runs 外,所有调用都需要具有写入权限的密钥。Webhook 密钥只会在设置它的调用中作为 webhookSecret 返回一次。arc.monitor() 是另一项功能:工作区的任务队列,可查看排队中和运行中的任务(见下文)。

表格上的 Agent

**尚未在 mesharc.dev 上线:**本节中的所有内容都需要具备这些功能的 API,目前这些功能位于 API 的功能分支上。

table = "Company,Website\nAcme,acme.com\nGlobex,globex.com\n"
ask = dict(csv=table, max_credits_per_row=300, urls=["{{Website}}"],
           schema={"type": "object", "properties": {"plan": {"type": "string"}, "price_month": {"type": "number"}}})
print(arc.agent_batches.preview("What does {{Company}} charge for its cheapest plan?", **ask)["estimate"])
batch = arc.agent_batches.start("What does {{Company}} charge for its cheapest plan?", **ask, concurrency=3)
arc.agent_batches.wait(batch["id"])
for row in arc.agent_batches.rows(batch["id"]):
    print(row["values"]["Company"], row["status"], row["data"])
arc.agent_batches.export(batch["id"], "prices.csv")

每行运行一个 Agent:提示词和起始 URL 从该行的列中填入,写作 {{column}};答案由 schema 塑造,类型为对象,并成为该行的新列。将表格作为 csv 或 rows 发送,后者是对象列表。必须提供 max_credits_per_row:每行到达上限时即停止,只按实际使用量付费;整个批次的花费绝不会超过行数 × 上限,如果开始时的余额更低,则不会超过该余额——无法再负担的行会被标记为 skipped,且不收费。如果不设置 allowed_domains,每行只能访问其自身 URL 所属的网站;设置为 [] 则不设限制。preview 会检查所有内容并估算费用,但不会启动任何任务。每行的 login_id 和 data_connections 均按 agent() 的方式传入。

方法功能
agent_batches.preview(prompt, csv=, rows=, max_credits_per_row=, ...)查看将要运行的内容、无法运行的行及原因、首批提示词和费用估算;免费
agent_batches.start(prompt, ..., webhook=None, idempotency_key=None)启动批次;webhookSecret 只返回一次,webhook 会收到一次 agent.batch.finished 事件
agent_batches.get(id, offset=0, limit=50, status=None) · agent_batches.rows(id, status=None) · agent_batches.wait(id)获取一页行、所有行;阻塞等待批次结束
agent_batches.list() · agent_batches.cancel(id) · agent_batches.export(id, path, fmt="csv")查看批次;停止一个批次;将包含答案的表格保存为文件(可能以公式开头的 CSV 单元格会作为文本写入)

批次会保留 7 天,之后 get 会抛出 MeshArcError 410 expired。

触发器、提案和月度费用

**尚未在 mesharc.dev 上线:**本节中的所有内容都需要具备这些功能的 API,目前这些功能位于 API 的功能分支上。

arc.triggers.create(
    {"kind": "project", "id": project["id"]},
    {"type": "agent", "prompt": "Summarise what changed on {{url}}", "maxCredits": 300},
    when={"kinds": ["modified"], "paths": ["/pricing*"]},
    limits={"cooldownMinutes": 60, "maxPerDay": 5},
)
for p in arc.proposals.list():                 # what agents proposed to watch, waiting
    print(p["kind"], p["name"], p["estimate"])
    arc.proposals.approve(p["id"])             # or arc.proposals.dismiss(p["id"])
print(arc.estimate("agent", "weekly", max_credits=300))   # about what a month costs, before saving
方法功能
triggers.create(source, action, when=None, limits=None, name=None, status=None)当项目变更或监控运行符合条件时,启动 Agent 运行、重新抓取、监控运行或重新检查
triggers.list(source_id=None, target_id=None) · triggers.get(id) · triggers.update(id, ...) · triggers.delete(id)查看和管理触发器
triggers.fires(id) · triggers.preview(id)查看每次来源运行匹配并启动的内容(保留 90 天);查看最新一次运行会如何处理,但不触发任何操作
proposals.list(status="proposed", run_id=None) · proposals.approve(id) · proposals.dismiss(id)批准后会创建项目或监控(监控的首次运行会立即启动)
estimate(kind, schedule, project_id=, monitor_id=, max_pages=, max_tier=, max_credits=, month_cap=)估算按计划运行的抓取、搜索或 Agent 每月约需多少积分;尚未运行时返回 upTo

子账户

**尚未在 mesharc.dev 上线:**本节中的所有内容都需要具备这些功能的 API,目前这些功能位于 API 的功能分支上。

Scale 或 Enterprise 工作区可以为自己的客户创建工作区:每个工作区都有自己的密钥、项目和数据,并在各自的额度上限内使用当前工作区的积分。每次调用都需要管理员密钥。

acme = arc.sub_accounts.create("Acme Ltd", monthly_cap=50_000, external_id="cust-42", idempotency_key="cust-42")
key = arc.sub_accounts.create_key(acme["id"], name="acme", scopes=["read", "write"])   # key["key"], once: for Acme

with arc.as_sub_account(acme["id"]) as acme_arc:      # your key, acting on Acme: X-MeshArc-Tenant on every request
    acme_arc.scrape("https://example.com")
arc.sub_accounts.update(acme["id"], suspended=True, reason="unpaid")   # refuses its keys, cancels what it runs
方法功能
sub_accounts.list() · sub_accounts.get(id){data, allowed, spent, remaining};查看单个子账户的月份、按类型划分的支出、费用,以及你的密钥具有的访问权限
sub_accounts.create(name, monthly_cap=None, external_id=None, idempotency_key=None) · sub_accounts.update(id, name=, monthly_cap=, suspended=, reason=)创建子账户;重命名、设置额度上限(0:无上限)、暂停或恢复
sub_accounts.keys(id) · sub_accounts.create_key(id, ...) · sub_accounts.revoke_key(id, key_id)管理供客户使用的专属密钥
as_sub_account(id) · MeshArc(key, tenant=id)创建客户端,使其每次请求都以该子账户的身份执行

连接和已保存的登录信息

尚未在 mesharc.dev 上线: 本节中的所有功能都需要使用具备这些特性的 API,目前这些特性位于 API 的功能分支上。

pdl = arc.connections.create("pdl", name="Company data", secrets={"api_key": "..."})
print(arc.connections.test(pdl["id"]))   # {status: healthy | failing, error, detail}
print(arc.logins.list()["enabled"])      # saved logins: made in the app, from a live browser

connections 是工作区的连接,包括目标、模型和数据提供方:providers()、list()、create(provider, name=, config=, secrets=)、get(id)、update(id, ...)、delete(id) 和 test(id)。密钥会发送给 API,但不会返回。logins 用于列出工作区已保存的登录信息(list()),并对其执行 update(id, name=, expired=)、test(id) 和 delete(id);登录信息保存在应用中,并通过实时浏览器创建,其会话绝不会返回。两者都需要管理员密钥。

监控网站:项目和运行

project = arc.projects.create(
    "https://docs.example.com",
    name="Docs",
    schedule="weekly",                                        # manual | hourly | daily | weekly
    config={"max_pages": 300, "include_paths": ["/docs/*"]},
)

run = arc.runs.start(project["id"], wait=True)                # the first run
# ...a week later, or arc.runs.start again: the second run produces the change record

record = arc.changes(project["id"])
print(record["change"]["counts"])   # {'added': …, 'removed': …, 'modified': …, 'withheld': …}

diff = arc.page_diff(project["id"], "https://docs.example.com/pricing")
方法功能
projects.list() · projects.get(id) · projects.update(id, name=, schedule=, retention=, config=) · projects.delete(id)项目
runs.list(project_id) · runs.start(project_id, wait=) · runs.wait(project_id, run_id) · runs.get(project_id, run_id) · runs.cancel(project_id, run_id)运行
pages(project_id, run_id=None) · page(project_id, url, run_id=None)某次运行的页面;完整查看单个页面
changes(project_id, run_id=None) · page_diff(project_id, url, run_id=None)变更记录;单个页面的逐词差异
search(project_id, q, mode="content" | "selector", run_id=None)哪些页面提及这些内容(词语、"phrases")或包含这些内容(CSS / XPath)
recrawl(project_id, urls)立即重新抓取这些页面
sources(project_id)种子 URL、站点地图、URL 列表、信息源和模式,以及上次运行通过各来源发现的内容
export(project_id, path, dataset="pages", fmt="jsonl", run_id=None, urls=None)将数据集(pages、markdown、changes、fields、sitemap)以 jsonl 或 csv 格式流式写入文件
arc.export(project["id"], "pages.csv", dataset="pages", fmt="csv")

工作区

me = arc.me()               # the workspace, its plan and limits, credits used and remaining, what this key may do
usage = arc.usage()         # pages per day, this month by engine
queue = arc.monitor()       # the job queue: what is queued and running (not arc.monitors)
meta = arc.meta()           # verdict meanings, engine costs, the config defaults
keys = arc.keys()
key = arc.create_key("ci", scopes=["read", "write"], projects=[project["id"]], expires_in_days=90)   # key["key"], once
arc.revoke_key(key["id"])

错误

所有失败都会引发 MeshArcError:

from mesharc import MeshArc, MeshArcError

try:
    arc.crawl("https://example.com", limit=1_000_000)
except MeshArcError as exc:
    print(exc.status, exc.code, exc.detail, exc.request_id)
code状态码含义
validation400 / 422请求中有内容不正确;detail 会说明具体问题
unauthorized401未提供密钥,或密钥已撤销或过期
plan_limit402套餐不包含此功能,或额度已用完
forbidden403密钥的作用域不允许此操作
not_found404不存在此对象,或此密钥无权查看
conflict409请求与当前状态冲突
expired410Agent 运行已超过保留期限(7 天)
rate_limited429超出密钥的速率限制;X-RateLimit-Reset 会说明何时重置
internal500联系支持时请提供 request_id

request_id 是 API 添加到响应和自身日志中的 ID,因此联系支持时只需提供这个字符串。

另有两种情况:网络故障或请求超时会引发 MeshArcError,其 status == 0,且 code 为 network 或 timeout;客户端停止等待的任务会引发 MeshArcTimeoutError——它既是 MeshArcError,也是 TimeoutError——其中包含 job_id,方便你稍后轮询任务(arc.get_crawl(id)、arc.batch(id)、arc.get_search(id)、arc.get_agent(id))。

幂等性与超时

  • scrape、scrape_one、crawl、web_search、agent、AgentRun.continue_、AgentRun.follow_up、monitors.create、monitors.run、agent_batches.start 和 sub_accounts.create 接受 idempotency_key=:在 24 小时内再次发送相同的键,会返回首次的结果,而不是创建第二个任务。MCP 服务的 search_web 工具特意不发送幂等键:已完成的搜索在一小时内再次执行相同搜索时,无论如何都会从缓存返回结果。它的 run_agent、continue_agent 和 create_monitor 工具会自行发送幂等键,根据输入和十分钟时间窗口生成,因此在同一十分钟内重试会命中同一次运行,之后重新运行则会创建新的运行。
  • 等待调用接受 poll=(轮询间隔秒数)和 timeout=(触发 TimeoutError 前的秒数),大多数还接受 wait=:wait=False 会立即返回信封。各调用的默认值不同:scrape、batch、crawl(以及 Crawl.wait、Crawl.pages)和 runs.start / runs.wait 每隔 3 秒轮询一次,最长等待一小时;scrape_one 和 web_search 每隔 2 秒轮询一次,最长等待十分钟;map / map_details 和 extract 每隔 2 秒轮询一次,最长等待五分钟;AgentRun.wait 和 AgentRun.trace 每隔 2 秒轮询一次,最长等待一小时。
  • 单次抓取的 timeout_s 表示 API 保持请求打开的时长(默认 60 秒,最长 120 秒);对于加载较慢的页面,会返回一个 ID,供后续轮询。
  • MeshArc(..., timeout=150.0) 是每个请求的 HTTP 超时。请求在安全可重试时会重试(适用于 GET、DELETE,或带有幂等键的 POST),遇到 429、502、503、504 和网络故障时,最多重试 max_retries 次(默认 2 次),并遵循 Retry-After。

点数

每个响应都会说明费用:页面上的 credits、任务信封中的 creditsUsed、HTTP 响应中的 X-MeshArc-Credits。页面按读取它的引擎计费——普通抓取 1 点,渲染 4 点;被拒绝的页面或 404 不收费。网页搜索结果页按读取它的引擎计费;如果所有引擎都拒绝,则不收费。已完成的搜索在一小时内(新闻搜索为十分钟)再次进行相同搜索(查询词、新闻或网页、结果页、国家/地区、语言、新鲜度和域名均相同),结果页会从缓存返回,不收费;但抓取的页面无论如何都会收费。Agent 运行会按读取的每个页面收费(被拒绝的页面免费),并按模型提供商的价格收取模型 Token 费用,另加 20%(使用工作区自有的大模型连接时,每 1,000 个 Token 收取 1 点),费用受其 max_credits 限制(默认 2,000 点);creditsUsed 统计这两项费用。监控器的运行按其搜索和 Agent 运行的费用计费。计费安排和套餐见 mesharc.dev/docs/billing。

MCP 服务

此软件包还提供 MeshArc MCP 服务,因此 Claude Desktop、Claude Code、Cursor 以及任何 MCP 客户端都可以将抓取、爬取、映射、网页搜索、向 Agent 提交问题以及读取变更记录作为工具使用。需要 Python 3.10 及以上版本。

我们提供托管服务,因此大多数人无需安装任何内容:

# Claude Code
claude mcp add --transport http mesharc https://mcp.mesharc.dev/mcp

它会打开浏览器一次,让你登录 mesharc.dev 并授权,之后即可使用。授权后会为应用签发专属 API 密钥,你可以在 Settings → API keys 中查看和撤销;除非你允许进行更改,否则该密钥为只读。任何支持远程 MCP URL 的客户端——Claude.ai 和 ChatGPT 连接器、Claude mobile、Cursor——都可以用相同方式连接。

或者,使用你自己的密钥,通过 stdio 自行运行:

pip install "mesharc[mcp]"
MESHARC_API_KEY=mesharc_... mesharc-mcp          # serves over stdio

# Claude Code
claude mcp add mesharc -e MESHARC_API_KEY=mesharc_... -- mesharc-mcp

工具:scrape_urls、map_site、crawl_site、search_web、list_projects、get_project、create_project、update_project、delete_project、start_run、list_runs、list_pages、get_changes、get_job、cancel_job、run_agent、continue_agent、create_monitor、list_monitors、pause_monitor 和 get_monitor_rows(最后六个目前尚未在 mesharc.dev 上线:它们依赖 API 的 Agent 和监控功能,该功能位于其功能分支)。create_monitor 会按计划定期执行网页搜索或 Agent 提问,并将每次运行的结果与上次比较;list_monitors 会显示监控项及其运行记录,pause_monitor 可暂停或恢复某个监控项,get_monitor_rows 可读取 Agent 监控所跟踪的记录。create_project 接受 max_credits_per_run 和 max_credits_per_month。get_project 会显示项目的 webhook 及其近期投递记录,update_project 可设置 webhook 并向其发送测试(test_webhook=true)。每个工具都是通过此客户端调用的,其回答均由输出架构描述。工具调用失败时会返回 {error, status};如果 API 提供了 code 和 request_id,也会一并返回。

包含许多页面的结果只是地图,并非疆域本身:爬取结果会返回已读取页面的索引(最多 500 个;如预算需要留出空间,则会更少)、在最多 60,000 个字符的预算内提供的摘录(最多 50 个),以及每个页面的链接数量,而不是链接本身。使用 get_job(kind="crawl", id=…, url=…) 单独请求所需页面,或通过 cursor= 继续爬取。多 URL 抓取超出预算时,会按状态统计其余页面,并列出所有未成功返回的页面。单独请求一个页面时——使用带 url 的 list_pages,或使用单个 url 调用 scrape_urls(设置 full=true 可获取完整格式)——会返回完整页面,最多 12,000 个字符。search_web 的回答也会限制在相同的 60,000 个字符内,其中抓取结果页面以摘录形式呈现;使用 scrape_urls 并设置 full=true 可完整读取其中一个页面。Agent 的回答也有相同限制:超出预算时,会先省略 fieldSources(回答中各个值的来源),然后截断来源列表,保留前 50 个(如果会占用超过一半空间,则更少),最后才会将 data 替换为其 JSON 文本并进行截断,同时注明完整回答所在位置——GET /api/v1/agent/{id},或在此客户端中调用 get_agent(id)。

在托管模式下,连接可以获批为只读,而只读模式有一点值得注意:它可以读取整个工作区,但无法访问网站。任何会获取数据的操作都是写操作,因为它会离开工作区,通常还会消耗额度——map_site 只读取网站的站点地图,每个站点地图文件消耗 1 个额度,并与其余操作一样受到限制。list_projects、get_project、list_runs、list_pages、get_changes、get_job、list_monitors 和 get_monitor_rows 可以使用;会获取数据或写入的 13 个工具——scrape_urls、map_site、crawl_site、search_web、run_agent、continue_agent、create_monitor、pause_monitor、create_project、update_project、delete_project、start_run、cancel_job——会返回 {"code": "read_only"} 并说明如何处理,直到重新连接应用并勾选“Also allow changes”(写入权限)。delete_project 不仅需要写入权限,还需要管理员 API 密钥,而已连接的应用不会持有此密钥,因此只能在本地使用此类密钥运行。

在托管模式下,对于需要保持连接数分钟的工具——爬取、等待运行完成的请求、多 URL 抓取、网页搜索——在 MESHARC_MCP_WAIT 秒后(默认为 25 秒,因为许多 MCP 主机会更早超时)会返回一个任务。工作会在服务器端继续执行;get_job 可获取任务状态,cancel_job 可停止爬取、运行、批处理或 Agent 运行。对于会抓取页面的搜索,如果搜索结果已返回但页面仍在陆续到达,回答会包含搜索结果和一个用于获取页面的任务。在本地运行时,这些工具会像以往一样阻塞。 run_agent 会让指定了起始 urls 的 Agent 留在这些 URL 所属的网站上,除非 allowed_domains 另有规定,应用也是如此:Agent 读取的页面可能会指示它前往其他地方。allowed_domains=[] 表示可以读取任何网站;如果没有 urls,就没有任何限制。客户端的 agent() 会按收到的内容发送,因为 API 就是这样接收参数的:不提供 allowed_domains 不代表没有限制。

无论托管还是本地运行,run_agent 都不会等待:Agent 需要运行几分钟,因此它会立即返回一个作业,以及截至目前本次运行已花费的额度。使用 get_job(kind="agent", id=…) 跟踪作业——每隔 30 到 60 秒查询一次——作业完成后会返回结果;cancel_job(kind="agent", id=…) 会在 Agent 进行下一步之前停止它,但已使用的额度仍会扣除。运行记录会保留 7 天;之后,get_job 会返回 status: "expired"。如果重试时该运行已经完成,则会返回其结果。如果运行因达到预算上限而停止(status: "credit_limit",已找到的内容位于 data.partial),可通过 continue_agent(id=…, max_credits=…) 继续:它会在同一线程上启动新运行,从停止处继续,并保留已读取的页面,不会再次收费。它也会以相同方式返回新运行的作业。如果运行并非因达到上限而停止,则会以代码 conflict 拒绝;如果再次请求已继续过的运行——包括超过 10 分钟的幂等键有效期后重试——则会返回继续运行的作业,不会启动新运行。

自行托管
MESHARC_MCP_PUBLIC_URL=https://mcp.example.dev/mcp MESHARC_OAUTH_ISSUER=https://api.example.dev MESHARC_INTROSPECT_SECRET=...                      mesharc-mcp --http --host 127.0.0.1 --port 8040

服务端不保存任何密钥。每个请求都携带调用方的 OAuth 令牌;令牌会根据 MESHARC_OAUTH_ISSUER 验证,然后用于发起调用,因此一个进程可以服务多个工作区。在此模式下不会读取 MESHARC_API_KEY;如果设置了该变量,启动时会提示它将被忽略。

在反向代理后运行时,请注意,绑定 127.0.0.1 会使 MCP SDK 启用 DNS 重绑定保护,并仅允许 Host 为 localhost。因此,服务会根据 MESHARC_MCP_PUBLIC_URL 配置自己的允许列表;如果未设置该变量,所有经代理的请求都会被拒绝。使用 MESHARC_MCP_ALLOWED_ORIGINS(以逗号分隔)将基于浏览器的客户端添加到 Origin 允许列表中。

隐私与安全

客户端只与一个主机通信——API 基础 URL,除非 MESHARC_API_URL 或 base_url= 指定其他地址,否则为 https://api.mesharc.dev——不会与其他主机通信。密钥只通过 bearer 标头传输,且仅使用 HTTPS。不会向磁盘写入任何内容,也不会发送遥测数据。客户端只读取 MESHARC_API_KEY 和 MESHARC_API_URL;HTTP 模式下的 MCP 服务会读取 MESHARC_MCP_PUBLIC_URL、MESHARC_OAUTH_ISSUER、MESHARC_INTROSPECT_SECRET、MESHARC_MCP_WAIT 和 MESHARC_MCP_ALLOWED_ORIGINS,并且不使用自己的密钥。

MeshArc 会保存哪些关于你和你抓取的页面的信息,以及保存多久,请参阅隐私政策。有关服务安全措施,请参阅安全页面。如需报告此客户端或服务中的漏洞,请发送邮件至 [email protected],而不要公开提交 issue——请参阅 SECURITY.md。

其他信息

客户端是一个轻量封装:每个方法对应一次 API 调用,并以 dict 形式返回 API 的 JSON。完整参考文档见 mesharc.dev/docs/api。使用完毕后调用 arc.close(),或将客户端用作上下文管理器。

MIT。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。