{"slug": "keenable-select-an-agent-that-searches-the-web-in-sql", "title": "Keenable SELECT: an agent that searches the web in SQL", "summary": "Keenable has launched Keenable SELECT, an MCP server that lets users query the live web with read-only DuckDB SQL, running one SELECT statement to search over 1,000 pages, filter with exact WHERE clauses at no LLM cost, and extract fields with a single small LLM call per row. The tool, demonstrated in a gallery of research reports, pairs a research agent that iteratively runs SQL queries with a report agent that generates shareable HTML pages from the result sets.", "body_md": "Research reports built by [Keenable SELECT](https://app.keenable.ai/select/start), an agent that searches the web in SQL.\n\nEvery card links the finished report and the full trajectory behind it: each query, tool result, and result set.\n\nYou ask\n\n“Which AI researchers moved between frontier labs since 2025? For each move list the researcher, the lab they left, where they went and the month.”\n\nKeenable SELECT runs SQL on the web\n\n```\nSELECT\n  SEM_EXTRACT(content, 'researcher'),\n  SEM_EXTRACT(content, 'left lab'),\n  SEM_EXTRACT(content, 'joined lab'),\n  SEM_EXTRACT(content, 'move month')\nFROM WEB_SEARCH(8 diverse queries)\nWHERE SEM_MATCH(content,\n  'named researcher moving\n   between frontier labs, 2025+')\n```\n\nKeenable SELECT is an MCP server with one main tool: `select`\n\n. The tool runs\none read-only DuckDB `SELECT`\n\nstatement on live web data. The server runs the\nweb and semantic operators outside DuckDB, puts their output back into the\nrow set, and then runs the final SQL in DuckDB.\n\nA traditional web search gives an agent ten links. The agent must then read each\npage and build the answer from expensive tokens. SELECT moves this work into\nthe query. One call can search more than 1,000 pages, filter them with an\nexact `WHERE`\n\nclause at no LLM cost, extract fields with one small LLM call\nper row, and group the rows.\n\n`select`\n\ntakes DuckDB `SELECT`\n\nqueries and returns the rows. The\nserver saves every query result as a result set with an id, and a later\nquery can read from that id.`generate_html_report`\n\ntakes a brief and result set ids. A report model on\nthe server writes an HTML report from the rows and returns a shareable\nlink.The operators live inside normal SQL. The server finds them in the parsed statement, runs them, and replaces them with plain columns. Exact SQL filters run first, so only the surviving rows go to the LLM operators.\n\n| Operator | What it does |\n|---|---|\n`WEB_SEARCH('q1', 'q2', ...)` | Searches all queries at the same time, merges ranked results, and removes repeated URLs. |\n`WEB_FETCH('https://a.com', ...)` | Gets the given URLs as Markdown, one row per page. |\n`SEM_EXTRACT(column, 'field description')` | One LLM call per row. It returns one field, or null when the text does not give the value. |\n`SEM_EXTRACT_ALL(column, 'what one value is')` | Like `SEM_EXTRACT` , but returns all matching values in a list. |\n`SEM_MATCH(column, 'predicate')` | An LLM test per row. Use it as a meaning-based `WHERE` filter. |\n`SEM_SCORE(column, 'query')` | A low-cost embedding score per row. Use `ORDER BY ... DESC LIMIT k` . |\n`SEM_NORM(column)` | Gives the same key to values with the same meaning. Use it in `GROUP BY` . |\n\n`WEB_SEARCH`\n\nand `WEB_FETCH`\n\ncan also run per row. Their arguments can use\nrow columns, for example `WEB_SEARCH(name || ' founding year')`\n\n.\n\nEvery report in this gallery comes from two agents: a research agent that\nuses the MCP server to gather the data, and a report agent that runs inside\n`generate_html_report`\n\non the server and writes the page.\n\nThe research agent is a plain tool loop: an LLM with the `select`\n\ntool. It\nwrites and runs its own queries until it can answer, and streams its tool\ncalls, results, and answer as events. A follow-up question continues the\nconversation on top of the stored transcript. Every run in this showcase asks\nfor an HTML report, so the agent ends each answer with the report link.\n\nA second agent writes each report on the server. It gets the brief, the rows of the result sets, and an authoring guide. It builds the page in a sandboxed Python session that holds the result sets as dataframes, so the data reaches the page without the model retyping it. After each publish, the server renders the draft and returns screenshots and the page's JavaScript error count; the agent fixes the document and publishes again, under a fixed budget. Only the final draft stays live, published as a link.", "url": "https://wpnews.pro/news/keenable-select-an-agent-that-searches-the-web-in-sql", "canonical_source": "https://keenableai.github.io/select-showcase/", "published_at": "2026-09-01 15:41:03+00:00", "updated_at": "2026-09-01 16:25:04.989920+00:00", "lang": "en", "topics": ["ai-tools", "ai-agents", "developer-tools"], "entities": ["Keenable", "Keenable SELECT", "DuckDB", "MCP"], "alternates": {"html": "https://wpnews.pro/news/keenable-select-an-agent-that-searches-the-web-in-sql", "markdown": "https://wpnews.pro/news/keenable-select-an-agent-that-searches-the-web-in-sql.md", "text": "https://wpnews.pro/news/keenable-select-an-agent-that-searches-the-web-in-sql.txt", "jsonld": "https://wpnews.pro/news/keenable-select-an-agent-that-searches-the-web-in-sql.jsonld"}}