browse-ai 0.1.1 → 0.1.2
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/dist/index.js +54 -6
- package/dist/setup.js +20 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { z } from "zod";
|
|
|
5
5
|
import { Readability } from "@mozilla/readability";
|
|
6
6
|
import { parseHTML } from "linkedom";
|
|
7
7
|
// --- Constants (inlined for standalone npm package) ---
|
|
8
|
-
const VERSION = "0.1.
|
|
8
|
+
const VERSION = "0.1.2";
|
|
9
9
|
const LLM_MODEL = "google/gemini-2.5-flash";
|
|
10
10
|
const LLM_ENDPOINT = "https://openrouter.ai/api/v1/chat/completions";
|
|
11
11
|
const TAVILY_ENDPOINT = "https://api.tavily.com/search";
|
|
@@ -24,8 +24,9 @@ if (args.includes("--help") || args.includes("-h")) {
|
|
|
24
24
|
browse-ai --version Show version
|
|
25
25
|
|
|
26
26
|
Environment Variables:
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
BROWSE_API_KEY BrowseAI Dev API key (get one at https://browseai.dev/dashboard)
|
|
28
|
+
SERP_API_KEY Tavily API key (get one at https://tavily.com) — BYOK mode
|
|
29
|
+
OPENROUTER_API_KEY OpenRouter API key (get one at https://openrouter.ai) — BYOK mode
|
|
29
30
|
|
|
30
31
|
MCP Tools:
|
|
31
32
|
browse.search Search the web for information
|
|
@@ -35,9 +36,15 @@ if (args.includes("--help") || args.includes("-h")) {
|
|
|
35
36
|
browse.compare Compare raw LLM vs evidence-backed answer
|
|
36
37
|
|
|
37
38
|
Quick Setup:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
Option A: Use a BrowseAI API key (one key for everything)
|
|
40
|
+
1. Sign in at https://browseai.dev and generate an API key
|
|
41
|
+
2. Run: npx browse-ai setup
|
|
42
|
+
3. Restart Claude Desktop
|
|
43
|
+
|
|
44
|
+
Option B: Bring your own keys (BYOK)
|
|
45
|
+
1. Get API keys: https://tavily.com + https://openrouter.ai
|
|
46
|
+
2. Run: npx browse-ai setup
|
|
47
|
+
3. Restart Claude Desktop
|
|
41
48
|
`);
|
|
42
49
|
process.exit(0);
|
|
43
50
|
}
|
|
@@ -52,8 +59,28 @@ else {
|
|
|
52
59
|
// --- Start MCP server ---
|
|
53
60
|
startServer();
|
|
54
61
|
}
|
|
62
|
+
// --- API mode (BrowseAI Dev API key) ---
|
|
63
|
+
const BROWSE_API_KEY = process.env.BROWSE_API_KEY;
|
|
64
|
+
const BROWSE_API_URL = process.env.BROWSE_API_URL || "https://ai-agent-browser.vercel.app/api";
|
|
65
|
+
const API_MODE = !!BROWSE_API_KEY;
|
|
66
|
+
async function apiCall(path, body) {
|
|
67
|
+
const res = await fetch(`${BROWSE_API_URL}${path}`, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers: {
|
|
70
|
+
"Content-Type": "application/json",
|
|
71
|
+
"X-API-Key": BROWSE_API_KEY,
|
|
72
|
+
},
|
|
73
|
+
body: JSON.stringify(body),
|
|
74
|
+
});
|
|
75
|
+
const data = await res.json();
|
|
76
|
+
if (!res.ok || !data.success)
|
|
77
|
+
throw new Error(data.error || `API failed: ${res.status}`);
|
|
78
|
+
return data.result;
|
|
79
|
+
}
|
|
55
80
|
// --- Env validation ---
|
|
56
81
|
function getEnvKeys() {
|
|
82
|
+
if (API_MODE)
|
|
83
|
+
return { SERP_API_KEY: "", OPENROUTER_API_KEY: "" };
|
|
57
84
|
const SERP_API_KEY = process.env.SERP_API_KEY;
|
|
58
85
|
const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY;
|
|
59
86
|
if (!SERP_API_KEY || !OPENROUTER_API_KEY) {
|
|
@@ -64,6 +91,7 @@ function getEnvKeys() {
|
|
|
64
91
|
${!OPENROUTER_API_KEY ? " OPENROUTER_API_KEY - Get one at https://openrouter.ai" : " OPENROUTER_API_KEY - Set"}
|
|
65
92
|
|
|
66
93
|
Quick fix: run 'npx browse-ai setup' to configure automatically.
|
|
94
|
+
Or use a BrowseAI API key: BROWSE_API_KEY=bai_xxx npx browse-ai
|
|
67
95
|
`);
|
|
68
96
|
process.exit(1);
|
|
69
97
|
}
|
|
@@ -302,18 +330,30 @@ function startServer() {
|
|
|
302
330
|
version: VERSION,
|
|
303
331
|
});
|
|
304
332
|
server.tool("browse_search", "Search the web for information on a topic. Returns URLs, titles, snippets, and relevance scores.", { query: z.string(), limit: z.number().optional() }, async ({ query, limit }) => {
|
|
333
|
+
if (API_MODE) {
|
|
334
|
+
const result = await apiCall("/browse/search", { query, limit: limit ?? 5 });
|
|
335
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
336
|
+
}
|
|
305
337
|
const results = await tavilySearch(query, limit ?? 5);
|
|
306
338
|
return {
|
|
307
339
|
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
308
340
|
};
|
|
309
341
|
});
|
|
310
342
|
server.tool("browse_open", "Fetch and parse a web page into clean text using Readability. Strips ads, nav, and boilerplate.", { url: z.string() }, async ({ url }) => {
|
|
343
|
+
if (API_MODE) {
|
|
344
|
+
const result = await apiCall("/browse/open", { url });
|
|
345
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
346
|
+
}
|
|
311
347
|
const page = await fetchPage(url);
|
|
312
348
|
return {
|
|
313
349
|
content: [{ type: "text", text: JSON.stringify(page, null, 2) }],
|
|
314
350
|
};
|
|
315
351
|
});
|
|
316
352
|
server.tool("browse_extract", "Extract structured knowledge (claims + sources + confidence) from a single web page using AI.", { url: z.string(), query: z.string().optional() }, async ({ url, query }) => {
|
|
353
|
+
if (API_MODE) {
|
|
354
|
+
const result = await apiCall("/browse/extract", { url, query });
|
|
355
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
356
|
+
}
|
|
317
357
|
const page = await fetchPage(url);
|
|
318
358
|
const domain = new URL(url).hostname;
|
|
319
359
|
const pageContent = `[Source 1] URL: ${url}\nTitle: ${page.title}\n\n${page.content.slice(0, MAX_PAGE_CONTENT_LENGTH)}`;
|
|
@@ -324,12 +364,20 @@ function startServer() {
|
|
|
324
364
|
};
|
|
325
365
|
});
|
|
326
366
|
server.tool("browse_answer", "Full deep research pipeline: search the web, fetch pages, extract claims, build evidence graph, and generate a structured answer with citations and confidence score.", { query: z.string() }, async ({ query }) => {
|
|
367
|
+
if (API_MODE) {
|
|
368
|
+
const result = await apiCall("/browse/answer", { query });
|
|
369
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
370
|
+
}
|
|
327
371
|
const result = await answerPipeline(query);
|
|
328
372
|
return {
|
|
329
373
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
330
374
|
};
|
|
331
375
|
});
|
|
332
376
|
server.tool("browse_compare", "Compare a raw LLM answer (no sources) vs an evidence-backed answer. Shows the difference between hallucination-prone and grounded responses.", { query: z.string() }, async ({ query }) => {
|
|
377
|
+
if (API_MODE) {
|
|
378
|
+
const result = await apiCall("/browse/compare", { query });
|
|
379
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
380
|
+
}
|
|
333
381
|
const [rawAnswer, evidenceResult] = await Promise.all([
|
|
334
382
|
rawLLMAnswer(query),
|
|
335
383
|
answerPipeline(query),
|
package/dist/setup.js
CHANGED
|
@@ -24,24 +24,32 @@ export async function runSetup() {
|
|
|
24
24
|
================
|
|
25
25
|
Configure browse-ai for Claude Desktop / Cursor / Windsurf
|
|
26
26
|
`);
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
const browseKey = await ask(" BrowseAI API key (leave blank to use your own Tavily + OpenRouter keys): ");
|
|
28
|
+
let mcpEnv;
|
|
29
|
+
if (browseKey.trim()) {
|
|
30
|
+
mcpEnv = { BROWSE_API_KEY: browseKey.trim() };
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
else {
|
|
33
|
+
const serpKey = await ask(" Tavily API key (get one at https://tavily.com): ");
|
|
34
|
+
if (!serpKey.trim()) {
|
|
35
|
+
console.log("\n Tavily API key is required. Get one at https://tavily.com\n");
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const openrouterKey = await ask(" OpenRouter API key (get one at https://openrouter.ai): ");
|
|
39
|
+
if (!openrouterKey.trim()) {
|
|
40
|
+
console.log("\n OpenRouter API key is required. Get one at https://openrouter.ai\n");
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
mcpEnv = {
|
|
44
|
+
SERP_API_KEY: serpKey.trim(),
|
|
45
|
+
OPENROUTER_API_KEY: openrouterKey.trim(),
|
|
46
|
+
};
|
|
36
47
|
}
|
|
37
48
|
rl.close();
|
|
38
49
|
const mcpEntry = {
|
|
39
50
|
command: "npx",
|
|
40
51
|
args: ["-y", "browse-ai"],
|
|
41
|
-
env:
|
|
42
|
-
SERP_API_KEY: serpKey.trim(),
|
|
43
|
-
OPENROUTER_API_KEY: openrouterKey.trim(),
|
|
44
|
-
},
|
|
52
|
+
env: mcpEnv,
|
|
45
53
|
};
|
|
46
54
|
const configPath = getConfigPath();
|
|
47
55
|
console.log(`\n Config path: ${configPath}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browse-ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Open-source deep research MCP server for AI agents. Search the web, extract claims, build evidence graphs, get structured answers with citations.",
|
|
6
6
|
"keywords": [
|