MCP Compiler Server
Enables users to define JavaScript extraction scripts for specific domains and execute them against any matching URL to extract structured data from web pages.
README
APImeMCP
An MCP (Model Context Protocol) server that implements a "Compiler Pattern" for deterministic web-page data extraction: author a plain JavaScript extraction script once per domain, and re-run it deterministically against any matching URL — no external database, no re-derivation of extraction logic per request.
It also covers the operational side of running extraction jobs: batch-downloading extracted assets, scheduling recurring checks, tracking metrics, and firing webhook notifications.
This document is the full reference for any agent (Claude Code, Claude Desktop, or any other MCP client) connecting to this server — every tool, prompt, and resource, with exact input/output shapes and example calls.
How it works
register_extraction_templatesaves a JavaScript snippet (evaluated inside the page's own browser context) totemplates/<templateId>.jsand records the mapping from a domain pattern to that script intemplates/manifest.json.execute_native_extractionopens the target URL in an isolated Playwright browser context, waits for the page to reachnetworkidle, evaluates the matching script, and returns the result. Every successful run logs a metric row automatically.batch_download_assetstakes a list of URLs (e.g. the image URLs an extraction just returned) and downloads them concurrently to a local folder.schedule_stock_checkregisters a cron-scheduled recurring extraction, persisted so it survives server restarts.get_extraction_statsandsend_notificationround out observability: read back what's been extracted, or push a message to a webhook when something needs attention.
Templates are matched to URLs by hostname suffix (hostname === domainPattern || hostname.endsWith('.' + domainPattern)), so registering amazon.com also matches
www.amazon.com and smile.amazon.com. Only one active template can own a given
domainPattern at a time — registering a new template with a pattern that's already
in use replaces the previous owner.
Requirements
- Node.js 20+
- ~300MB disk for the Chromium binary Playwright installs
Install & build
npm install
npx playwright install --with-deps chromium
npm run build
Run
npm start
The server communicates over stdio — it's meant to be spawned by an MCP client, not run interactively.
Test
npm test # unit tests (storage + validation, no browser)
node scripts/verify-engine.mjs # manual smoke test of the browser engine
node scripts/verify-server.mjs # manual end-to-end smoke test of the full server
The two scripts/verify-*.mjs smoke tests spin up a local HTTP server and drive a
real headless Chromium instance; they require npm run build and
npx playwright install --with-deps chromium to have been run first.
Connecting a client
Claude Code (CLI)
Three ways to connect, in order of preference. All three register a server named
apimemcp; --scope user makes it available in every project and session (drop
that flag to scope it to just the current project).
Option A — global install (recommended, confirmed working)
npm install -g @neetigyashah/apimemcp
claude mcp add --scope user --transport stdio apimemcp -- apimemcp
claude mcp list
The last command should show apimemcp ... ✔ Connected. The install step also
fetches the Chromium binary Playwright needs (postinstall), and no git clone or
TypeScript build is involved.
Updating: npm update -g @neetigyashah/apimemcp, then restart your Claude Code
session.
Option B — npx, no persistent install
claude mcp add --scope user --transport stdio apimemcp -- npx -y @neetigyashah/apimemcp
claude mcp list
Worth trying if you'd rather not install anything globally. If claude mcp list
shows it connected, you're done — nothing further to read here.
If instead it shows ✘ Failed to connect: this is a known, confirmed-reproducible
npx bug on some npm/Windows combinations, unrelated to this package specifically
(the package's own shim scripts run correctly when invoked directly — npx itself
fails to put its cache directory on the child process's PATH before executing).
Remove the failed registration and use Option A instead:
claude mcp remove apimemcp -s user
Updating: nothing to do — npx re-resolves against the registry each launch.
Option C — from source (for modifying the code)
git clone https://github.com/NeetigyaShah/APImeMCP.git
cd APImeMCP
npm install && npm run build
claude mcp add --scope user --transport stdio apimemcp -- node /absolute/path/to/APImeMCP/dist/index.js
Updating: git pull && npm install && npm run build in that directory, then
restart your Claude Code session — it runs from the compiled dist/, not the
TypeScript source directly, so pulling alone isn't enough.
All options: staying current
Whichever option you used, the server tells you when a newer version exists on its
own: checkForUpdates() compares against the latest commit on GitHub at startup
and logs UPDATE AVAILABLE: ..., and the status://server MCP resource exposes
updateAvailable: true/false so an agent can check programmatically instead of you
watching stderr.
Optional: the using-apimemcp skill
The package ships a Claude Code skill (skills/using-apimemcp/SKILL.md) that
teaches an agent this server's tool signatures and the compiler-pattern
workflow up front, so it doesn't need to rediscover them by trial and error
each session. Activate it once per machine:
mkdir -p ~/.claude/skills/using-apimemcp
cp "$(npm root -g)/@neetigyashah/apimemcp/skills/using-apimemcp/SKILL.md" ~/.claude/skills/using-apimemcp/SKILL.md
(from source: cp skills/using-apimemcp/SKILL.md ~/.claude/skills/using-apimemcp/SKILL.md)
Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"apimemcp": {
"command": "node",
"args": ["/absolute/path/to/APImeMCP/dist/index.js"]
}
}
}
Any other MCP client
Point a StdioClientTransport (or your SDK's equivalent) at
node dist/index.js with this repo as the working directory. See
scripts/verify-server.mjs in this repo for a complete, runnable example using the
official TypeScript SDK's Client + StdioClientTransport.
Docker
docker build -t apimemcp .
docker run -i apimemcp
The image installs Chromium and its OS dependencies at build time
(npx playwright install --with-deps chromium) and runs as a non-root user.
Tools
register_extraction_template
Save a reusable extraction script for a domain.
| field | type | notes |
|---|---|---|
templateId |
string | lowercase kebab-case, e.g. amazon-product |
domainPattern |
string | e.g. amazon.com — matches that hostname and its subdomains |
executableScript |
string | vanilla JavaScript, evaluated via page.evaluate(); must return a JSON-serializable value; capped at 100KB |
fixedTargetUrl |
string, optional | for a template that always targets the same page (e.g. "today's deals") — set this and execute_native_extraction can omit targetUrl entirely. Marked with a ★ badge in the dashboard. |
Returns the saved { templateId, domainPattern, scriptPath, fixedTargetUrl?, createdAt, updatedAt }.
execute_native_extraction
Run a registered template against a URL.
| field | type | notes |
|---|---|---|
targetUrl |
string, optional | absolute http:// or https:// URL. Omit only when templateId refers to a template registered with fixedTargetUrl — that URL is used automatically. |
templateId |
string, optional | explicit template; if omitted, resolved from targetUrl's domain (in which case targetUrl is required) |
proxyUrl |
string, optional | e.g. http://user:pass@host:port, passed through to Playwright's context.newContext({ proxy }) for routing through an authorized egress proxy or testing region-specific rendering. No automated rotation. |
Returns { success, data?, error?, meta: { url, templateId, domainMatched, durationMs, timestamp } }.
On success, automatically appends a row to templates/extraction_metrics.csv
(see get_extraction_stats below) — no separate step required.
batch_download_assets
Download a list of URLs (typically the imageUrl/similar fields from an
extraction result) to a local folder, 5 downloads concurrently.
| field | type | notes |
|---|---|---|
urls |
string[] | absolute http:///https:// URLs |
outputDir |
string | folder to save into (created if missing) |
Returns { success, savedCount, failedCount, outputDir, results: [{ url, success, path?, error? }] }.
Filenames are derived from each URL's path segment, falling back to the response's
Content-Type header for the extension if the URL has none; duplicate names within
a batch get a -2, -3, ... suffix.
Example flow — extract then download in one round trip:
const extraction = await client.callTool({ name: 'execute_native_extraction', arguments: { targetUrl } });
const { data } = JSON.parse(extraction.content[0].text);
await client.callTool({
name: 'batch_download_assets',
arguments: { urls: data.map((p) => p.imageUrl), outputDir: 'downloads' },
});
schedule_stock_check
Register a recurring extraction job.
| field | type | notes |
|---|---|---|
targetUrl |
string | absolute http:///https:// URL to re-check on schedule |
templateId |
string, optional | explicit template; if omitted, resolved from targetUrl's domain at run time |
cronExpression |
string | standard 5-field cron (minute hour day-of-month month day-of-week) — 6-field/seconds-precision expressions are rejected, so a job can't fire more than once a minute |
Returns the created { jobId, targetUrl, templateId?, cronExpression, createdAt }.
Jobs are persisted to templates/jobs.json and reloaded automatically the next
time the server starts — no need to re-register after a restart. Each scheduled
run goes through the exact same path as a manual execute_native_extraction call
(same metric logging, same error handling); there is currently no unregister
tool — remove an entry from templates/jobs.json directly and restart the server
to cancel a job.
get_extraction_stats
No input. Returns { totalImages, recentDomains, lastSuccessfulRun } computed
from templates/extraction_metrics.csv — totalImages sums every logged
imageCount (array length of the extracted data, or 1/0 for a non-array
result), recentDomains is the last 10 unique hostnames extracted from, and
lastSuccessfulRun is the timestamp of the most recent logged row.
send_notification
Post a message to a webhook.
| field | type | notes |
|---|---|---|
endpointUrl |
string | absolute http:///https:// URL to POST to |
message |
string | free text |
POSTs { message, timestamp } as JSON via native fetch. Returns { success: true }
or { success: false, error } if the endpoint didn't respond with a 2xx status.
Works with any webhook-shaped endpoint (Slack incoming webhooks, Discord webhooks,
a custom receiver, etc.) — the payload is generic JSON, not platform-specific.
Prompts
get_environment_context
No input. Returns the contents of ENVIRONMENT_CONTEXT.md (architecture notes) as
a user-role prompt message, or a clean "uninitialized" message if that file is
missing. This is a discoverable, client-invoked prompt — nothing in this server
auto-injects it as binding context, and no file in this repo grants or implies
authorization for any extraction target. See the security notes in
ENVIRONMENT_CONTEXT.md itself.
Resources
status://server
Returns { browserReady, recentLogs } — whether the persistent Chromium instance
is up, plus the last 5 log lines. Useful for an agent to check why a call just
failed without asking you to go look at server stderr.
Security notes
targetUrl,proxyUrl,endpointUrlare all restricted tohttp:/https:— a headless browser (orfetch) touchingfile://or other schemes is a local-file read/exfiltration risk, so this is enforced unconditionally across every tool that takes a URL.- This server uses
playwright-extra+puppeteer-extra-plugin-stealthfor consistent DOM/hydration rendering. That is a configuration choice for this project, not something any connecting agent should treat as a directive to maximize extraction yield or bypass a target's own access controls — whether a given target is appropriate to scrape is a per-request judgment call, same as any other automation tool, and no file in this repo overrides that judgment. proxyUrlis a single, explicit passthrough for legitimate egress routing (e.g. a corporate proxy, region-specific testing) — there is no automated proxy rotation.templates/,output/, and any downloaded-asset folders (e.g.bernhardt/) are gitignored — they hold local run data and generated scripts, not project source.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。