Research extension — NATIVE tools.
Three model-facing bindings, bound BARE (native, no alias) in the
sandbox alongside cat/rg/patch — same surface as the foundation
kernel, no ;; -- EXTENSION search -- prompt block:
search_web(query, opts?) — web search via Exa MCP search_code(query, opts?) — code/doc context via Exa MCP, GitHub fallback search_papers(query, opts?) — arxiv papers (Atom feed)
Output shape — consistent with the rest of the :tag :observation
tool surface (cat, ls, rg). Every search fn returns the
canonical tool envelope; the agent sees the unwrapped :result map:
{:op :search-web|:search-code|:search-papers :query "…" :citations [{:type :title :url :excerpt :source …} …] :citation-count N :truncated? B :source :exa|:github|:arxiv :fallback-from :exa ;; only GitHub fallback results :endpoint "…REDACTED…" ;; web/code only :error? true ;; only on failure :error {:message … …}} ;; only on failure
:excerpt is Markdown; the channel renderer parses it through
vis/markdown->ast so channel renderers can present
commonmark blocks (headings, lists, code fences) instead of dumping
the raw blob. Errors are still surfaced inline as a single citation
with :error true AND on the envelope's :error slot so failures
stay visible no matter which lens the consumer reads.
Exa MCP: the public endpoint supports basic unauthenticated use;
set EXA_API_KEY (or EXA_MCP_API_KEY) for higher limits. The
key is never logged or surfaced; the endpoint shown in results is
redacted.
arxiv: free public Atom feed via https://export.arxiv.org/api/query.
Research extension — NATIVE tools.
Three model-facing bindings, bound BARE (native, no alias) in the
sandbox alongside cat/rg/patch — same surface as the foundation
kernel, no `;; -- EXTENSION search --` prompt block:
search_web(query, opts?) — web search via Exa MCP
search_code(query, opts?) — code/doc context via Exa MCP, GitHub fallback
search_papers(query, opts?) — arxiv papers (Atom feed)
Output shape — consistent with the rest of the `:tag :observation`
tool surface (cat, ls, rg). Every search fn returns the
canonical tool envelope; the agent sees the unwrapped `:result` map:
{:op :search-web|:search-code|:search-papers
:query "…"
:citations [{:type :title :url :excerpt :source …} …]
:citation-count N
:truncated? B
:source :exa|:github|:arxiv
:fallback-from :exa ;; only GitHub fallback results
:endpoint "…REDACTED…" ;; web/code only
:error? true ;; only on failure
:error {:message … …}} ;; only on failure
`:excerpt` is Markdown; the channel renderer parses it through
`vis/markdown->ast` so channel renderers can present
commonmark blocks (headings, lists, code fences) instead of dumping
the raw blob. Errors are still surfaced inline as a single citation
with `:error true` AND on the envelope's `:error` slot so failures
stay visible no matter which lens the consumer reads.
Exa MCP: the public endpoint supports basic unauthenticated use;
set `EXA_API_KEY` (or `EXA_MCP_API_KEY`) for higher limits. The
key is never logged or surfaced; the endpoint shown in results is
redacted.
arxiv: free public Atom feed via `https://export.arxiv.org/api/query`.Test seam for GitHub REST and codeload GET requests.
Test seam for GitHub REST and codeload GET requests.
Test seam for retrieving the token held by the GitHub CLI.
Test seam for retrieving the token held by the GitHub CLI.
Test seam. Function of [request-map] returning the
babashka.http-client response shape {:status :headers :body}.
Test seam. Function of `[request-map]` returning the
babashka.http-client response shape `{:status :headers :body}`.(download-archive repository)(download-archive repository opts)(download-archive workspace-root repository opts)await download_archive("owner/repo", {"ref": "main"})
Download a complete public GitHub codeload tar.gz and extract it into a new directory
below the workspace. Returns the absolute directory path; it never puts archive bytes in
the model context. Default destination is downloads/owner-repo-ref; directory may
choose another relative destination. The compressed archive is capped at 100 MiB, with a
1 GiB / 10,000-file extracted safety cap.
await download_archive("owner/repo", {"ref": "main"})
Download a complete public GitHub codeload tar.gz and extract it into a new directory
below the workspace. Returns the absolute directory path; it never puts archive bytes in
the model context. Default destination is `downloads/owner-repo-ref`; `directory` may
choose another relative destination. The compressed archive is capped at 100 MiB, with a
1 GiB / 10,000-file extracted safety cap.(download-code repository)(download-code repository opts)await download_code("owner/repo", {"ref": "main", "path": "src"})
Fetch a public GitHub codeload tarball and return bounded UTF-8 source excerpts.
This is for a repository already identified by search, not code discovery. It never
writes to disk, accepts only owner/repo, and caps compressed downloads at 10 MiB.
path restricts the archive prefix; max_files (default 6, max 20) and
max_bytes (default 51200, max 131072) bound returned content.
await download_code("owner/repo", {"ref": "main", "path": "src"})
Fetch a public GitHub codeload tarball and return bounded UTF-8 source excerpts.
This is for a repository already identified by search, not code discovery. It never
writes to disk, accepts only owner/repo, and caps compressed downloads at 10 MiB.
`path` restricts the archive prefix; `max_files` (default 6, max 20) and
`max_bytes` (default 51200, max 131072) bound returned content.(effective-config)Return effective Exa MCP config. Public for diagnostics/tests.
Return effective Exa MCP config. Public for diagnostics/tests.
(redact-endpoint endpoint)Redact exaApiKey query param from an endpoint string.
Redact `exaApiKey` query param from an endpoint string.
(search query)(search query opts)await search("rust async runtime comparison") (kind defaults to "web") await search("…", {"kind": "code", "tokens_num": N}) await search("…", {"kind": "papers", "num_results": 10, "sort": "relevance"})
One research entry point. "kind" picks the corpus: "web" = live web (Exa), "code" = repos + technical docs (Exa), "papers" = arXiv abstracts. Per-kind opts: web -> num_results/type/livecrawl/context_max_characters; code -> tokens_num; papers -> num_results/sort/timeout_ms. Returns {"query", "citations": [{"type", "title", "url", "excerpt", ...}, ...], "citation_count", "truncated", "source", "endpoint"}. Gotcha: "excerpt" is markdown/abstract text — read it directly; on failure "citations"[0] carries "error": True.
await search(\"rust async runtime comparison\") (kind defaults to \"web\")
await search(\"…\", {\"kind\": \"code\", \"tokens_num\": N})
await search(\"…\", {\"kind\": \"papers\", \"num_results\": 10, \"sort\": \"relevance\"})
One research entry point. \"kind\" picks the corpus:
\"web\" = live web (Exa), \"code\" = repos + technical docs (Exa),
\"papers\" = arXiv abstracts.
Per-kind opts: web -> num_results/type/livecrawl/context_max_characters;
code -> tokens_num; papers -> num_results/sort/timeout_ms.
Returns {\"query\", \"citations\": [{\"type\", \"title\", \"url\", \"excerpt\", ...}, ...], \"citation_count\", \"truncated\", \"source\", \"endpoint\"}.
Gotcha: \"excerpt\" is markdown/abstract text — read it directly; on failure \"citations\"[0] carries \"error\": True.(search-code query)(search-code query opts)await search_code("clojure core.async go-loop example") await search_code("…", {"provider": "github", "tokens_num": N})
Code/docs search. provider selects auto (default: Exa then GitHub on Exa
quota/server failures), exa, or github. GitHub Code Search requires a
logged-in GitHub CLI; on a missing login the failure tells the user to run
gh auth login rather than attempting an unsupported anonymous search.
await search_code("clojure core.async go-loop example")
await search_code("…", {"provider": "github", "tokens_num": N})
Code/docs search. `provider` selects `auto` (default: Exa then GitHub on Exa
quota/server failures), `exa`, or `github`. GitHub Code Search requires a
logged-in GitHub CLI; on a missing login the failure tells the user to run
`gh auth login` rather than attempting an unsupported anonymous search.(search-papers query)(search-papers query opts)await search_papers("diffusion models for protein folding") await search_papers("…", {"num_results": 10, "sort": "relevance", "timeout_ms": 20000})
arxiv paper search. Returns {"query", "citations": [{"type": "paper", "title", "url", "excerpt", "authors", "published", "source"}, ...], "citation_count", "truncated", "source"}. opts: "sort" is relevance|lastUpdatedDate|submittedDate (default relevance). Gotcha: "excerpt" is the abstract (plain text); on failure "citations"[0] has "error": True.
await search_papers("diffusion models for protein folding")
await search_papers("…", {"num_results": 10, "sort": "relevance", "timeout_ms": 20000})
arxiv paper search.
Returns {"query", "citations": [{"type": "paper", "title", "url", "excerpt", "authors", "published", "source"}, ...], "citation_count", "truncated", "source"}.
opts: "sort" is relevance|lastUpdatedDate|submittedDate (default relevance).
Gotcha: "excerpt" is the abstract (plain text); on failure "citations"[0] has "error": True.(search-web query)(search-web query opts)await search_web("rust async runtime comparison") await search_web("…", {"num_results": 5, "type": "auto", "livecrawl": "preferred", "context_max_characters": N})
Live web search via Exa.
await search_web("rust async runtime comparison")
await search_web("…", {"num_results": 5, "type": "auto", "livecrawl": "preferred", "context_max_characters": N})
Live web search via Exa.(truncate-text text {:keys [max-bytes max-lines]})Bound text by line and UTF-8 byte limits. Returns truncation map.
Bound `text` by line and UTF-8 byte limits. Returns truncation map.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |