feat: add web_search tool to Atlas using Jina AI free search
Atlas can now search the internet during product discovery conversations to research competitors, pricing models, and market context. Uses Jina AI's free search endpoint — no API key required. Made-with: Cursor
This commit is contained in:
@@ -5,5 +5,5 @@ registerAgent({
|
|||||||
description: 'PRD agent — guides users through structured product discovery and produces a comprehensive requirements document',
|
description: 'PRD agent — guides users through structured product discovery and produces a comprehensive requirements document',
|
||||||
model: 'A', // Gemini Flash — fast, conversational, cost-effective for dialogue
|
model: 'A', // Gemini Flash — fast, conversational, cost-effective for dialogue
|
||||||
promptId: 'atlas',
|
promptId: 'atlas',
|
||||||
tools: pick(['finalize_prd'])
|
tools: pick(['web_search', 'finalize_prd'])
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import './agent';
|
|||||||
import './memory';
|
import './memory';
|
||||||
import './skills';
|
import './skills';
|
||||||
import './prd';
|
import './prd';
|
||||||
|
import './search';
|
||||||
|
|
||||||
// Re-export the public API — identical surface to the old tools.ts
|
// Re-export the public API — identical surface to the old tools.ts
|
||||||
export { ALL_TOOLS, executeTool, ToolDefinition } from './registry';
|
export { ALL_TOOLS, executeTool, ToolDefinition } from './registry';
|
||||||
|
|||||||
54
src/tools/search.ts
Normal file
54
src/tools/search.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { registerTool } from './registry';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Web search via Jina AI's free search endpoint (s.jina.ai).
|
||||||
|
* No API key required. Returns clean, AI-readable markdown results.
|
||||||
|
* Atlas uses this for competitor research, market context, pricing models, etc.
|
||||||
|
*/
|
||||||
|
registerTool({
|
||||||
|
name: 'web_search',
|
||||||
|
description: 'Search the web for current information. Use this to research competitors, market trends, pricing models, existing solutions, technology choices, or any topic the user mentions that would benefit from real-world context. Returns a summary of top search results.',
|
||||||
|
parameters: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
query: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'The search query. Be specific — e.g. "SaaS project management tools pricing 2024" rather than just "project management".'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['query']
|
||||||
|
},
|
||||||
|
async handler(args) {
|
||||||
|
const query = String(args.query).trim();
|
||||||
|
if (!query) return { error: 'No query provided' };
|
||||||
|
|
||||||
|
const url = `https://s.jina.ai/${encodeURIComponent(query)}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
'Accept': 'text/plain',
|
||||||
|
'X-Return-Format': 'markdown',
|
||||||
|
},
|
||||||
|
signal: AbortSignal.timeout(15_000),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
return { error: `Search failed with status ${res.status}` };
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = await res.text();
|
||||||
|
|
||||||
|
// Jina returns verbose results — truncate to avoid flooding the context window
|
||||||
|
const truncated = text.length > 6000 ? text.slice(0, 6000) + '\n\n[...results truncated]' : text;
|
||||||
|
|
||||||
|
return {
|
||||||
|
query,
|
||||||
|
results: truncated,
|
||||||
|
};
|
||||||
|
} catch (err: unknown) {
|
||||||
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
|
return { error: `Search request failed: ${message}` };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user