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 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.0";
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
- SERP_API_KEY Tavily API key (get one at https://tavily.com)
28
- OPENROUTER_API_KEY OpenRouter API key (get one at https://openrouter.ai)
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
- 1. Get API keys: https://tavily.com + https://openrouter.ai
39
- 2. Run: npx browse-ai setup
40
- 3. Restart Claude Desktop
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 serpKey = await ask(" Tavily API key (get one at https://tavily.com): ");
28
- if (!serpKey.trim()) {
29
- console.log("\n Tavily API key is required. Get one at https://tavily.com\n");
30
- process.exit(1);
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
- const openrouterKey = await ask(" OpenRouter API key (get one at https://openrouter.ai): ");
33
- if (!openrouterKey.trim()) {
34
- console.log("\n OpenRouter API key is required. Get one at https://openrouter.ai\n");
35
- process.exit(1);
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.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": [