smart-web-mcp 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,25 @@ The format is based on Keep a Changelog and this project uses SemVer.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.5.0] - 2026-03-23
10
+
11
+ ### Added
12
+
13
+ - `smartcrawl` for local browser-driven exploration of boards, docs sites, pagination, and scroll-heavy pages
14
+ - shared stealth browser session runtime used by both `smartcrawl` and `smartfetch`
15
+ - npm-managed `pre-commit` and `pre-push` hook installation for local TypeScript and full repo checks
16
+
17
+ ### Changed
18
+
19
+ - configuration UX now centers on `SMART_WEB_PROFILE` with fine-grained env flags treated as advanced overrides
20
+ - README, examples, and getting-started docs now use one npm-first path instead of parallel npm/Bun setup tracks
21
+
22
+ ### Fixed
23
+
24
+ - MCP registry metadata now keeps the advanced environment-variable overrides that the runtime still supports
25
+ - Playwright fetch now preserves HTML content for provider normalization instead of dropping back to plain text first
26
+ - DCInside parsing now handles recommendation boxes with additional classes
27
+
9
28
  ## [0.4.4] - 2026-03-21
10
29
 
11
30
  ### Added
package/README.md CHANGED
@@ -2,9 +2,13 @@
2
2
 
3
3
  # smart-web
4
4
 
5
- A web MCP that works.
5
+ One local MCP server for web search, page fetch, and site crawl.
6
6
 
7
- `smart-web` bundles `smartsearch` and `smartfetch` into one local MCP server for Claude Code, OpenCode, and other MCP clients.
7
+ `smart-web` ships three tools in one stdio server:
8
+
9
+ - `smartsearch`: fallback web search with optional site-crawl handoff
10
+ - `smartfetch`: browser-aware fetch with target-specific normalization
11
+ - `smartcrawl`: site exploration for docs, boards, pagination, and scroll-heavy pages
8
12
 
9
13
  - npm: `https://www.npmjs.com/package/smart-web-mcp`
10
14
  - MCP Registry: `io.github.rich-jojo/smart-web`
@@ -16,11 +20,7 @@ A web MCP that works.
16
20
  npx -y smart-web-mcp
17
21
  ```
18
22
 
19
- If your team uses Bun:
20
-
21
- ```bash
22
- bunx smart-web-mcp
23
- ```
23
+ `npm` is the canonical package-manager path for this repo and package.
24
24
 
25
25
  ## Quick start
26
26
 
@@ -45,29 +45,48 @@ OpenCode:
45
45
  }
46
46
  ```
47
47
 
48
- ## What it includes
48
+ ## Configuration
49
+
50
+ Most users only need these variables:
51
+
52
+ - `SMART_WEB_PROFILE=balanced`: default behavior
53
+ - `SMART_WEB_PROFILE=private`: disables relay-style providers and public search helpers by default
54
+ - `SEARXNG_BASE_URL=https://search.example.com`: self-hosted private fallback search
55
+ - `EXA_API_KEY` and `BRAVE_SEARCH_API_KEY`: optional paid search tiers
56
+
57
+ Everything else is still supported, but treated as an advanced override rather than the main setup path.
49
58
 
50
- - `smartsearch`: fallback web search with Exa, Brave, SearXNG, DuckDuckGo, and Brave HTML
51
- - `smartsearch`: plus direct site-native fallbacks for Velog and StackExchange-style sites when that is more reliable
52
- - `smartfetch`: browser-aware fetch with special handling for Reddit, X, YouTube, DCInside, and Naver Blog
53
- - `smartfetch`: includes discovered media and attachment download URLs in structured output
54
- - sensible safety defaults: private-network blocking, local-only mode, provider toggles
59
+ ## Smoke test
60
+
61
+ - ask `smartsearch` for `site:velog.io react useeffect cleanup`
62
+ - ask `smartfetch` to fetch a normal article URL
63
+ - ask `smartcrawl` to explore a docs site or forum you actually use
55
64
 
56
65
  ## Docs
57
66
 
58
- - `docs/getting-started.md`
59
- - `docs/configuration.md`
60
- - `docs/providers.md`
61
- - `docs/architecture.md`
62
- - `docs/composition.md`
63
- - `docs/releasing.md`
64
- - `CHANGELOG.md`
67
+ - [Getting started](docs/getting-started.md)
68
+ - [Configuration](docs/configuration.md)
69
+ - [Crawling](docs/crawling.md)
70
+ - [Providers](docs/providers.md)
71
+ - [Architecture](docs/architecture.md)
72
+ - [Contributing](CONTRIBUTING.md)
73
+ - [CHANGELOG](CHANGELOG.md)
74
+
75
+ ## Examples
76
+
77
+ - [Claude Code](examples/claude.mcp.json)
78
+ - [OpenCode](examples/opencode.json)
79
+
80
+ These examples use `/absolute/path/to/...` placeholders for local checkouts.
65
81
 
66
- ## Status
82
+ ## Development
83
+
84
+ ```bash
85
+ npm install
86
+ npm run check
87
+ ```
67
88
 
68
- - npm package is published
69
- - MCP Registry metadata is published
70
- - GitHub Actions verify package, registry metadata, and MCP wiring
89
+ `npm install` also installs local git hooks for fast TypeScript checks on commit and the full verification suite on push.
71
90
 
72
91
  ## License
73
92
 
@@ -0,0 +1,41 @@
1
+ import type { ToolError } from "./shared.js";
2
+ type BrowserWaitUntil = "domcontentloaded" | "networkidle";
3
+ export type BrowserAnchor = {
4
+ href: string;
5
+ text: string;
6
+ context: string;
7
+ rel: string;
8
+ class_name: string;
9
+ aria_label: string;
10
+ };
11
+ export type BrowserPageSnapshot = {
12
+ ok: true;
13
+ method: "playwright_stealth";
14
+ url: string;
15
+ final_url: string;
16
+ title: string;
17
+ html: string;
18
+ text: string;
19
+ links: string[];
20
+ anchors: BrowserAnchor[];
21
+ scroll_steps: number;
22
+ load_more_clicks: number;
23
+ };
24
+ type BrowserPageFailure = {
25
+ ok: false;
26
+ method: "playwright_stealth";
27
+ error: ToolError;
28
+ };
29
+ export type BrowserPageResult = BrowserPageSnapshot | BrowserPageFailure;
30
+ export type BrowserSessionOptions = {
31
+ url: string;
32
+ timeoutMs: number;
33
+ allowPrivateHosts?: boolean;
34
+ waitUntil?: BrowserWaitUntil;
35
+ extraHeaders?: Record<string, string>;
36
+ scrollSteps?: number;
37
+ maxLoadMoreClicks?: number;
38
+ maxAnchors?: number;
39
+ };
40
+ export declare function runBrowserSession(options: BrowserSessionOptions): Promise<BrowserPageResult>;
41
+ export {};
@@ -0,0 +1,206 @@
1
+ import { asString, dedupeUrls, resolveValidatedUrl } from "./shared.js";
2
+ const DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36";
3
+ let chromiumCache = undefined;
4
+ async function getChromium() {
5
+ if (chromiumCache !== undefined)
6
+ return chromiumCache;
7
+ try {
8
+ const playwrightExtra = await import("playwright-extra");
9
+ const stealthModule = await import("puppeteer-extra-plugin-stealth");
10
+ const chromium = playwrightExtra.chromium;
11
+ const createStealthPlugin = stealthModule.default || stealthModule;
12
+ chromium.use(createStealthPlugin());
13
+ chromiumCache = chromium;
14
+ }
15
+ catch {
16
+ chromiumCache = false;
17
+ }
18
+ return chromiumCache;
19
+ }
20
+ async function launchBrowser(chromium) {
21
+ const attempts = [
22
+ { channel: process.env.SMARTFETCH_CHROME_CHANNEL || "chrome", headless: true },
23
+ { executablePath: process.env.SMARTFETCH_CHROME_PATH, headless: true },
24
+ { headless: true },
25
+ ];
26
+ let lastError = null;
27
+ for (const options of attempts) {
28
+ const launchOptions = { ...options };
29
+ if (launchOptions.executablePath === undefined)
30
+ delete launchOptions.executablePath;
31
+ try {
32
+ return await chromium.launch(launchOptions);
33
+ }
34
+ catch (error) {
35
+ lastError = error;
36
+ }
37
+ }
38
+ throw lastError;
39
+ }
40
+ async function clickLoadMore(page) {
41
+ return page.evaluate(() => {
42
+ const pattern = /^(load more|show more|more posts|more results|view more|see more|더보기|더 보기|더 불러오기)$/i;
43
+ const nodes = Array.from(document.querySelectorAll("button, [role='button'], input[type='button'], input[type='submit'], a"));
44
+ for (const node of nodes) {
45
+ const element = node;
46
+ const text = (element.innerText || element.getAttribute("aria-label") || element.getAttribute("title") || "").replace(/\s+/g, " ").trim();
47
+ if (!text || !pattern.test(text))
48
+ continue;
49
+ if (node instanceof HTMLAnchorElement) {
50
+ const href = node.getAttribute("href") || "";
51
+ if (href && href !== "#" && !href.toLowerCase().startsWith("javascript:"))
52
+ continue;
53
+ }
54
+ const rect = element.getBoundingClientRect();
55
+ if (rect.width <= 0 || rect.height <= 0)
56
+ continue;
57
+ element.click();
58
+ return true;
59
+ }
60
+ return false;
61
+ });
62
+ }
63
+ async function drivePage(page, options) {
64
+ const scrollSteps = Math.max(0, Math.min(20, Math.floor(Number(options.scrollSteps || 0))));
65
+ const maxLoadMoreClicks = Math.max(0, Math.min(10, Math.floor(Number(options.maxLoadMoreClicks || 0))));
66
+ let completedScrollSteps = 0;
67
+ let loadMoreClicks = 0;
68
+ let stagnantRounds = 0;
69
+ let previousAnchorCount = await page.locator("a[href]").count().catch(() => 0);
70
+ let previousHeight = await page.evaluate(() => document.body?.scrollHeight || document.documentElement?.scrollHeight || 0);
71
+ for (let step = 0; step < scrollSteps; step++) {
72
+ let clicked = false;
73
+ if (loadMoreClicks < maxLoadMoreClicks) {
74
+ clicked = await clickLoadMore(page).catch(() => false);
75
+ if (clicked) {
76
+ loadMoreClicks += 1;
77
+ await page.waitForTimeout(700);
78
+ }
79
+ }
80
+ await page.evaluate(() => window.scrollTo(0, document.body?.scrollHeight || document.documentElement?.scrollHeight || 0));
81
+ completedScrollSteps += 1;
82
+ await page.waitForTimeout(700);
83
+ await page.waitForLoadState("networkidle", { timeout: 1500 }).catch(() => { });
84
+ const currentAnchorCount = await page.locator("a[href]").count().catch(() => previousAnchorCount);
85
+ const currentHeight = await page.evaluate(() => document.body?.scrollHeight || document.documentElement?.scrollHeight || 0);
86
+ if (!clicked && currentAnchorCount <= previousAnchorCount && currentHeight <= previousHeight) {
87
+ stagnantRounds += 1;
88
+ }
89
+ else {
90
+ stagnantRounds = 0;
91
+ }
92
+ previousAnchorCount = currentAnchorCount;
93
+ previousHeight = currentHeight;
94
+ if (stagnantRounds >= 2)
95
+ break;
96
+ }
97
+ return { scroll_steps: completedScrollSteps, load_more_clicks: loadMoreClicks };
98
+ }
99
+ async function extractAnchors(page, maxAnchors) {
100
+ return page.evaluate((maxItems) => {
101
+ const nodes = Array.from(document.querySelectorAll("a[href]")).slice(0, maxItems);
102
+ return nodes.map((node) => {
103
+ const element = node;
104
+ const closest = element.closest("article, li, tr, section, main, .topic-list-item, .topic-body, .topic-post, .athing, .titleline, [role='article'], .feed-item, .timeline-item, .post, .storylink");
105
+ const text = (element.innerText || element.getAttribute("aria-label") || element.getAttribute("title") || "").replace(/\s+/g, " ").trim();
106
+ const contextSource = closest?.innerText || element.innerText || element.textContent || "";
107
+ return {
108
+ href: element.href || "",
109
+ text,
110
+ context: contextSource.replace(/\s+/g, " ").trim().slice(0, 500),
111
+ rel: element.rel || "",
112
+ class_name: typeof element.className === "string" ? element.className : "",
113
+ aria_label: element.getAttribute("aria-label") || "",
114
+ };
115
+ });
116
+ }, maxAnchors);
117
+ }
118
+ export async function runBrowserSession(options) {
119
+ const chromium = await getChromium();
120
+ if (!chromium) {
121
+ return {
122
+ ok: false,
123
+ method: "playwright_stealth",
124
+ error: {
125
+ category: "unavailable",
126
+ code: "playwright_unavailable",
127
+ message: "Playwright browser runtime is unavailable",
128
+ },
129
+ };
130
+ }
131
+ const resolveOptions = {
132
+ timeoutMs: options.timeoutMs,
133
+ };
134
+ if (options.allowPrivateHosts !== undefined)
135
+ resolveOptions.allowPrivateHosts = options.allowPrivateHosts;
136
+ if (options.extraHeaders !== undefined)
137
+ resolveOptions.headers = options.extraHeaders;
138
+ const resolved = await resolveValidatedUrl(options.url, resolveOptions);
139
+ if (!resolved.ok) {
140
+ return {
141
+ ok: false,
142
+ method: "playwright_stealth",
143
+ error: {
144
+ category: "block",
145
+ code: resolved.reason,
146
+ message: resolved.message,
147
+ },
148
+ };
149
+ }
150
+ let browser = null;
151
+ try {
152
+ browser = await launchBrowser(chromium);
153
+ const context = await browser.newContext({
154
+ userAgent: DEFAULT_USER_AGENT,
155
+ locale: "ko-KR",
156
+ extraHTTPHeaders: options.extraHeaders,
157
+ });
158
+ const page = await context.newPage();
159
+ await page.goto(resolved.url, {
160
+ waitUntil: options.waitUntil || "networkidle",
161
+ timeout: options.timeoutMs,
162
+ });
163
+ const driven = await drivePage(page, options);
164
+ const html = await page.content();
165
+ const text = await page.evaluate(() => document.body?.innerText || "");
166
+ const title = asString(await page.title());
167
+ const finalUrl = asString(page.url());
168
+ const maxAnchors = Math.max(50, Math.min(2000, Math.floor(Number(options.maxAnchors || 400))));
169
+ const anchors = await extractAnchors(page, maxAnchors);
170
+ const links = dedupeUrls(anchors.map((anchor) => asString(anchor?.href)));
171
+ await context.close();
172
+ await browser.close();
173
+ browser = null;
174
+ return {
175
+ ok: true,
176
+ method: "playwright_stealth",
177
+ url: options.url,
178
+ final_url: finalUrl || resolved.url,
179
+ title,
180
+ html,
181
+ text,
182
+ links,
183
+ anchors,
184
+ scroll_steps: driven.scroll_steps,
185
+ load_more_clicks: driven.load_more_clicks,
186
+ };
187
+ }
188
+ catch (error) {
189
+ const message = error instanceof Error ? error.message : String(error);
190
+ return {
191
+ ok: false,
192
+ method: "playwright_stealth",
193
+ error: {
194
+ category: message.toLowerCase().includes("timeout") ? "timeout" : "unavailable",
195
+ code: "playwright_fetch_failed",
196
+ message,
197
+ },
198
+ };
199
+ }
200
+ finally {
201
+ if (browser) {
202
+ await browser.close().catch(() => { });
203
+ }
204
+ }
205
+ }
206
+ //# sourceMappingURL=browser-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-session.js","sourceRoot":"","sources":["../src/browser-session.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAEvE,MAAM,kBAAkB,GAAG,iHAAiH,CAAA;AA8C5I,IAAI,aAAa,GAAQ,SAAS,CAAA;AAElC,KAAK,UAAU,WAAW;IACxB,IAAI,aAAa,KAAK,SAAS;QAAE,OAAO,aAAa,CAAA;IACrD,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;QACxD,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAA;QACpE,MAAM,QAAQ,GAAI,eAAuB,CAAC,QAAQ,CAAA;QAClD,MAAM,mBAAmB,GAAI,aAAqB,CAAC,OAAO,IAAI,aAAa,CAAA;QAC3E,QAAQ,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC,CAAA;QACnC,aAAa,GAAG,QAAQ,CAAA;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,aAAa,GAAG,KAAK,CAAA;IACvB,CAAC;IACD,OAAO,aAAa,CAAA;AACtB,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,QAAa;IACxC,MAAM,QAAQ,GAAG;QACf,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC9E,EAAE,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,QAAQ,EAAE,IAAI,EAAE;QACtE,EAAE,QAAQ,EAAE,IAAI,EAAE;KACnB,CAAA;IACD,IAAI,SAAS,GAAY,IAAI,CAAA;IAC7B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,aAAa,GAAG,EAAE,GAAG,OAAO,EAAE,CAAA;QACpC,IAAI,aAAa,CAAC,cAAc,KAAK,SAAS;YAAE,OAAQ,aAAyC,CAAC,cAAc,CAAA;QAChH,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;QAC7C,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,SAAS,GAAG,KAAK,CAAA;QACnB,CAAC;IACH,CAAC;IACD,MAAM,SAAS,CAAA;AACjB,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,IAAS;IACpC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;QACxB,MAAM,OAAO,GAAG,qFAAqF,CAAA;QACrG,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,wEAAwE,CAAC,CAAC,CAAA;QAC7H,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,IAAmB,CAAA;YACnC,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;YACzI,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,SAAQ;YAC1C,IAAI,IAAI,YAAY,iBAAiB,EAAE,CAAC;gBACtC,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;gBAC5C,IAAI,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC;oBAAE,SAAQ;YACrF,CAAC;YACD,MAAM,IAAI,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAA;YAC5C,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;gBAAE,SAAQ;YACjD,OAAO,CAAC,KAAK,EAAE,CAAA;YACf,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAS,EAAE,OAA8B;IAChE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3F,MAAM,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACvG,IAAI,oBAAoB,GAAG,CAAC,CAAA;IAC5B,IAAI,cAAc,GAAG,CAAC,CAAA;IACtB,IAAI,cAAc,GAAG,CAAC,CAAA;IACtB,IAAI,mBAAmB,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAA;IAC9E,IAAI,cAAc,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,IAAI,QAAQ,CAAC,eAAe,EAAE,YAAY,IAAI,CAAC,CAAC,CAAA;IAE1H,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,WAAW,EAAE,IAAI,EAAE,EAAE,CAAC;QAC9C,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,IAAI,cAAc,GAAG,iBAAiB,EAAE,CAAC;YACvC,OAAO,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAA;YACtD,IAAI,OAAO,EAAE,CAAC;gBACZ,cAAc,IAAI,CAAC,CAAA;gBACnB,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAA;YAChC,CAAC;QACH,CAAC;QAED,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,IAAI,QAAQ,CAAC,eAAe,EAAE,YAAY,IAAI,CAAC,CAAC,CAAC,CAAA;QACzH,oBAAoB,IAAI,CAAC,CAAA;QACzB,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAA;QAC9B,MAAM,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QAE7E,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,CAAA;QACjG,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,IAAI,QAAQ,CAAC,eAAe,EAAE,YAAY,IAAI,CAAC,CAAC,CAAA;QAC3H,IAAI,CAAC,OAAO,IAAI,kBAAkB,IAAI,mBAAmB,IAAI,aAAa,IAAI,cAAc,EAAE,CAAC;YAC7F,cAAc,IAAI,CAAC,CAAA;QACrB,CAAC;aAAM,CAAC;YACN,cAAc,GAAG,CAAC,CAAA;QACpB,CAAC;QACD,mBAAmB,GAAG,kBAAkB,CAAA;QACxC,cAAc,GAAG,aAAa,CAAA;QAC9B,IAAI,cAAc,IAAI,CAAC;YAAE,MAAK;IAChC,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAA;AACjF,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAS,EAAE,UAAkB;IACzD,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,QAAgB,EAAE,EAAE;QACxC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;QACjF,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACxB,MAAM,OAAO,GAAG,IAAyB,CAAA;YACzC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,kKAAkK,CAAuB,CAAA;YACzN,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;YACzI,MAAM,aAAa,GAAG,OAAO,EAAE,SAAS,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,WAAW,IAAI,EAAE,CAAA;YAC1F,OAAO;gBACL,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE;gBACxB,IAAI;gBACJ,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;gBAChE,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,EAAE;gBACtB,UAAU,EAAE,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;gBAC1E,UAAU,EAAE,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,EAAE;aACrD,CAAA;QACH,CAAC,CAAC,CAAA;IACJ,CAAC,EAAE,UAAU,CAAC,CAAA;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAA8B;IACpE,MAAM,QAAQ,GAAG,MAAM,WAAW,EAAE,CAAA;IACpC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,oBAAoB;YAC5B,KAAK,EAAE;gBACL,QAAQ,EAAE,aAAa;gBACvB,IAAI,EAAE,wBAAwB;gBAC9B,OAAO,EAAE,2CAA2C;aACrD;SACF,CAAA;IACH,CAAC;IAED,MAAM,cAAc,GAA8C;QAChE,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAA;IACD,IAAI,OAAO,CAAC,iBAAiB,KAAK,SAAS;QAAE,cAAc,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAA;IACzG,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS;QAAE,cAAc,CAAC,OAAO,GAAG,OAAO,CAAC,YAAY,CAAA;IACrF,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAA;IACvE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,oBAAoB;YAC5B,KAAK,EAAE;gBACL,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,QAAQ,CAAC,MAAM;gBACrB,OAAO,EAAE,QAAQ,CAAC,OAAO;aAC1B;SACF,CAAA;IACH,CAAC;IAED,IAAI,OAAO,GAAQ,IAAI,CAAA;IACvB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAA;QACvC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;YACvC,SAAS,EAAE,kBAAkB;YAC7B,MAAM,EAAE,OAAO;YACf,gBAAgB,EAAE,OAAO,CAAC,YAAY;SACvC,CAAC,CAAA;QACF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;QACpC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;YAC5B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,aAAa;YAC7C,OAAO,EAAE,OAAO,CAAC,SAAS;SAC3B,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAC7C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;QACjC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,IAAI,EAAE,CAAC,CAAA;QACtE,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;QAC1C,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QAC9F,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;QACtD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAqB,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;QACxF,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;QACrB,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;QACrB,OAAO,GAAG,IAAI,CAAA;QACd,OAAO;YACL,EAAE,EAAE,IAAI;YACR,MAAM,EAAE,oBAAoB;YAC5B,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,SAAS,EAAE,QAAQ,IAAI,QAAQ,CAAC,GAAG;YACnC,KAAK;YACL,IAAI;YACJ,IAAI;YACJ,KAAK;YACL,OAAO;YACP,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;SAC1C,CAAA;IACH,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACtE,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,oBAAoB;YAC5B,KAAK,EAAE;gBACL,QAAQ,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa;gBAC/E,IAAI,EAAE,yBAAyB;gBAC/B,OAAO;aACR;SACF,CAAA;IACH,CAAC;YAAS,CAAC;QACT,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACvC,CAAC;IACH,CAAC;AACH,CAAC"}
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import { readFileSync } from "node:fs";
3
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
  import { z } from "zod";
6
+ import { renderSmartcrawl, runSmartcrawl } from "./smartcrawl.js";
6
7
  import { runSmartfetch, renderSmartfetch } from "./smartfetch.js";
7
8
  import { runSmartsearch, renderSmartsearch } from "./smartsearch.js";
8
9
  const packageVersion = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
@@ -15,18 +16,72 @@ server.tool("smartsearch", "Search the web with fallback engines. Uses Exa, then
15
16
  numResults: z.number().int().min(1).max(20).optional().describe("Number of search results to return"),
16
17
  type: z.enum(["auto", "fast", "deep"]).optional().describe("Search depth hint"),
17
18
  contextMaxCharacters: z.number().int().min(1000).max(20000).optional().describe("Maximum snippet/context size hint"),
18
- }, async ({ query, numResults, type, contextMaxCharacters }) => {
19
+ site_crawl: z.boolean().optional().describe("After search, hand off into smartcrawl when a crawl_url or site: operator is available"),
20
+ crawl_url: z.string().optional().describe("Optional starting URL for the smartcrawl handoff"),
21
+ crawl_mode: z.enum(["auto", "board", "docs"]).optional().describe("Traversal hint for the smartcrawl handoff"),
22
+ crawl_limit: z.number().int().min(1).max(50).optional().describe("Maximum ranked crawl results to return in the handoff summary"),
23
+ crawl_max_pages: z.number().int().min(1).max(20).optional().describe("Maximum number of pages to visit during the crawl handoff"),
24
+ crawl_max_depth: z.number().int().min(0).max(3).optional().describe("Maximum recursive depth for the crawl handoff"),
25
+ crawl_timeout_ms: z.number().int().min(3000).max(120000).optional().describe("Per-page timeout for the crawl handoff"),
26
+ }, async ({ query, numResults, type, contextMaxCharacters, site_crawl, crawl_url, crawl_mode, crawl_limit, crawl_max_pages, crawl_max_depth, crawl_timeout_ms }) => {
19
27
  const output = await runSmartsearch({
20
28
  query,
21
29
  ...(numResults === undefined ? {} : { numResults }),
22
30
  ...(type === undefined ? {} : { searchType: type }),
23
31
  ...(contextMaxCharacters === undefined ? {} : { contextMaxCharacters }),
32
+ ...(site_crawl === undefined ? {} : { siteCrawl: site_crawl }),
33
+ ...(crawl_url === undefined ? {} : { crawlUrl: crawl_url }),
34
+ ...(crawl_mode === undefined ? {} : { crawlMode: crawl_mode }),
35
+ ...(crawl_limit === undefined ? {} : { crawlLimit: crawl_limit }),
36
+ ...(crawl_max_pages === undefined ? {} : { crawlMaxPages: crawl_max_pages }),
37
+ ...(crawl_max_depth === undefined ? {} : { crawlMaxDepth: crawl_max_depth }),
38
+ ...(crawl_timeout_ms === undefined ? {} : { crawlTimeoutMs: crawl_timeout_ms }),
24
39
  });
25
40
  return {
26
41
  structuredContent: output,
27
42
  content: [{ type: "text", text: renderSmartsearch(output) }],
28
43
  };
29
44
  });
45
+ server.tool("smartcrawl", "Explore a specific site with a browser, pagination, and scroll-aware traversal to gather relevant pages or posts.", {
46
+ url: z.string().min(1).describe("Starting URL to explore"),
47
+ query: z.string().optional().describe("Optional topic filter used to rank crawl results"),
48
+ mode: z.enum(["auto", "board", "docs"]).optional().describe("Traversal hint for boards, docs sites, or automatic detection"),
49
+ limit: z.number().int().min(1).max(50).optional().describe("Maximum number of ranked results to return"),
50
+ max_pages: z.number().int().min(1).max(20).optional().describe("Maximum number of pages to visit during exploration"),
51
+ max_depth: z.number().int().min(0).max(3).optional().describe("Maximum recursive depth from the starting URL"),
52
+ include_patterns: z.array(z.string().min(1)).max(20).optional().describe("Optional regex or substring patterns for URLs to keep"),
53
+ exclude_patterns: z.array(z.string().min(1)).max(20).optional().describe("Optional regex or substring patterns for URLs to skip"),
54
+ same_origin_only: z.boolean().optional().describe("Restrict traversal to the starting origin while still surfacing external item links"),
55
+ follow_pagination: z.boolean().optional().describe("Follow next or more-style pagination links during exploration"),
56
+ scroll_steps: z.number().int().min(0).max(12).optional().describe("How many scroll attempts to use for scroll-driven pages"),
57
+ timeout_ms: z.number().int().min(3000).max(120000).optional().describe("Crawl timeout budget per page"),
58
+ format: z.enum(["json", "text", "markdown"]).optional().describe("Text rendering format for the human-readable content"),
59
+ }, async ({ url, query, mode, limit, max_pages, max_depth, include_patterns, exclude_patterns, same_origin_only, follow_pagination, scroll_steps, timeout_ms, format, }) => {
60
+ const output = await runSmartcrawl({
61
+ url,
62
+ ...(query === undefined ? {} : { query }),
63
+ ...(mode === undefined ? {} : { mode }),
64
+ ...(limit === undefined ? {} : { limit }),
65
+ ...(max_pages === undefined ? {} : { maxPages: max_pages }),
66
+ ...(max_depth === undefined ? {} : { maxDepth: max_depth }),
67
+ ...(include_patterns === undefined ? {} : { includePatterns: include_patterns }),
68
+ ...(exclude_patterns === undefined ? {} : { excludePatterns: exclude_patterns }),
69
+ ...(same_origin_only === undefined ? {} : { sameOriginOnly: same_origin_only }),
70
+ ...(follow_pagination === undefined ? {} : { followPagination: follow_pagination }),
71
+ ...(scroll_steps === undefined ? {} : { scrollSteps: scroll_steps }),
72
+ ...(timeout_ms === undefined ? {} : { timeoutMs: timeout_ms }),
73
+ });
74
+ const requestedFormat = format || "json";
75
+ return {
76
+ structuredContent: output,
77
+ content: [
78
+ {
79
+ type: "text",
80
+ text: requestedFormat === "json" ? JSON.stringify(output, null, 2) : renderSmartcrawl(output, requestedFormat),
81
+ },
82
+ ],
83
+ };
84
+ });
30
85
  server.tool("smartfetch", "Fetch a URL with browser-aware fallbacks. Uses impit first, then stealth Playwright when the page looks JS-rendered or blocked.", {
31
86
  url: z.string().min(1).describe("Target URL to fetch"),
32
87
  target: z.enum(["auto", "generic", "reddit_post", "x_post", "dcinside_post", "youtube_video", "naver_blog", "tistory_post", "velog_post"]).optional().describe("Content type hint"),
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/shared.d.ts CHANGED
@@ -19,12 +19,33 @@ export type SearchResult = {
19
19
  snippet: string;
20
20
  engine: string;
21
21
  };
22
+ export type SearchHandoffItem = {
23
+ title: string;
24
+ url: string;
25
+ snippet: string;
26
+ kind: string;
27
+ score: number;
28
+ depth: number;
29
+ };
30
+ export type SearchHandoff = {
31
+ tool: "smartcrawl";
32
+ performed: boolean;
33
+ reason: string;
34
+ start_url: string;
35
+ query: string;
36
+ mode: "auto" | "board" | "docs";
37
+ items: SearchHandoffItem[];
38
+ pages_attempted: number;
39
+ pages_loaded: number;
40
+ errors: string[];
41
+ };
22
42
  export type SearchResponse = {
23
43
  engine: string;
24
44
  query: string;
25
45
  results: SearchResult[];
26
46
  fallbacks_tried: string[];
27
47
  notes: string[];
48
+ handoff?: SearchHandoff;
28
49
  };
29
50
  export type SmartfetchOutput = {
30
51
  source: "smartfetch";
@@ -58,6 +79,7 @@ export type SmartfetchAsset = {
58
79
  downloadable: boolean;
59
80
  };
60
81
  export type SmartfetchRenderFormat = "text" | "markdown";
82
+ export type SmartWebProfile = "balanced" | "private";
61
83
  export declare function asString(value: unknown): string;
62
84
  export declare function asNumber(value: unknown): number;
63
85
  export declare function decodeHtml(value: string): string;
@@ -81,8 +103,9 @@ export declare function extractMetaDescription(html: string): string;
81
103
  export declare function extractMetaProperty(html: string, property: string): string;
82
104
  export declare function extractMetaName(html: string, name: string): string;
83
105
  export declare function inferTarget(url: string, target: Target): ResolvedTarget;
106
+ export declare function smartWebProfile(): SmartWebProfile;
84
107
  export declare function envFlag(name: string): boolean;
85
- export declare function envEnabled(name: string, defaultValue: boolean): boolean;
108
+ export declare function envEnabled(name: string, defaultValue: boolean, profileDefaults?: Partial<Record<SmartWebProfile, boolean>>): boolean;
86
109
  export declare function validateOutboundUrl(url: string, options?: {
87
110
  allowPrivateHosts?: boolean;
88
111
  }): Promise<{
package/dist/shared.js CHANGED
@@ -245,7 +245,7 @@ export function inferTarget(url, target) {
245
245
  return "velog_post";
246
246
  }
247
247
  catch {
248
- // ignore invalid URL here; downstream fetch will surface a better error
248
+ // Defer invalid URL reporting to the fetch path so callers get one consistent error.
249
249
  }
250
250
  return "generic";
251
251
  }
@@ -267,13 +267,21 @@ function truthyEnv(value) {
267
267
  function falsyEnv(value) {
268
268
  return /^(0|false|no|off)$/i.test(String(value || "").trim());
269
269
  }
270
+ export function smartWebProfile() {
271
+ const value = String(process.env.SMART_WEB_PROFILE || "").trim().toLowerCase();
272
+ if (["private", "local", "local-only", "strict"].includes(value))
273
+ return "private";
274
+ return "balanced";
275
+ }
270
276
  export function envFlag(name) {
271
277
  return truthyEnv(process.env[name]);
272
278
  }
273
- export function envEnabled(name, defaultValue) {
279
+ export function envEnabled(name, defaultValue, profileDefaults) {
274
280
  const value = process.env[name];
275
- if (value === undefined || String(value).trim() === "")
276
- return defaultValue;
281
+ if (value === undefined || String(value).trim() === "") {
282
+ const profileDefault = profileDefaults?.[smartWebProfile()];
283
+ return profileDefault !== undefined ? profileDefault : defaultValue;
284
+ }
277
285
  if (truthyEnv(value))
278
286
  return true;
279
287
  if (falsyEnv(value))
@@ -310,7 +318,7 @@ export async function validateOutboundUrl(url, options) {
310
318
  }
311
319
  }
312
320
  catch {
313
- // Let fetch surface the eventual resolution error.
321
+ // DNS failures are reported by the fetch attempt itself.
314
322
  }
315
323
  return { ok: true };
316
324
  }
@@ -346,7 +354,7 @@ export async function resolveValidatedUrl(url, options) {
346
354
  await response.body?.cancel();
347
355
  }
348
356
  catch {
349
- // ignore body cancellation failures
357
+ // Ignore best-effort stream cleanup failures.
350
358
  }
351
359
  return { ok: true, url: currentUrl };
352
360
  }
@@ -355,7 +363,7 @@ export async function resolveValidatedUrl(url, options) {
355
363
  await response.body?.cancel();
356
364
  }
357
365
  catch {
358
- // ignore body cancellation failures
366
+ // Ignore best-effort stream cleanup failures.
359
367
  }
360
368
  return { ok: false, reason: "too_many_redirects", message: `Too many redirects while resolving URL: ${url}` };
361
369
  }
@@ -364,7 +372,7 @@ export async function resolveValidatedUrl(url, options) {
364
372
  await response.body?.cancel();
365
373
  }
366
374
  catch {
367
- // ignore body cancellation failures
375
+ // Ignore best-effort stream cleanup failures.
368
376
  }
369
377
  if (!location) {
370
378
  return { ok: false, reason: "redirect_missing_location", message: "Redirect response did not include a Location header" };