abyss-mcp
Provides web and academic paper search through a SearXNG instance, returning URL and snippet per result. Supports categories such as general or science, result limiting, and filtering of blocked domains.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@abyss-mcpsearch the web for what zram is and give me a few short results"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
llm-abyss
Opinionated tooling around the abyss of LLMs: composable, async-first retrieval steps, servable as MCP tools.
The repo has two parts:
abyss, the library. Small components (searchers, filters, and soon crawlers, chunkers and rankers) that snap together into streaming pipelines with>>. The core depends only onhttpx.Local serving. SearXNG in Docker plus a llama.cpp server whose web UI can call abyss pipelines as tools. See docs/DEPLOY.md.
Install
uv add "llm-abyss @ git+https://github.com/NISH1001/llm-abyss" # core
uv add "llm-abyss[mcp] @ git+https://github.com/NISH1001/llm-abyss" # + MCP serving and SettingsThe distribution is llm-abyss; the import is abyss. Try it without installing anything, against any
SearXNG instance with the JSON format enabled:
uv run --with "llm-abyss @ git+https://github.com/NISH1001/llm-abyss" python -c '
import asyncio
from abyss import SearxngSearcher, NotBlocked, Take
search = SearxngSearcher("http://127.0.0.1:4243") >> NotBlocked() >> Take(3)
for c in asyncio.run(search.run("what is zram")):
print(c.url, "|", c.text)'Related MCP server: groundlink-mcp
How it composes
Everything is a Step: something that turns an async stream of items into another async stream. >>
chains steps into a new step, and building a chain runs nothing.
search = SearxngSearcher(url) >> NotBlocked() >> Take(5)
chunks = await search.run("zram vs zswap") # list of everything
async for chunk in search.stream("zram vs zswap"): # items as they arrive
...Items stream. A step passes each result on the moment it is ready, so item 1 can be three steps downstream while item 5 is still in the first.
Take(n)stops early. Once it has n items it closes the stream and cancels the work still running upstream.Concurrency lives in the step. Async per-item steps run their items concurrently, with an optional
concurrency=cap.Plain functions chain too. A function becomes a
Map, sosearch >> (lambda c: c.url)works.
Data: one Chunk type
Every stage passes the same small record:
@dataclass
class Chunk:
url: str
text: str # snippet after search; page or window text after later stages
title: str = ""
score: float = 0.0
meta: dict = field(default_factory=dict) # query, engine, rank, date, ...Searchers tag each chunk with meta["query"], so later steps (a ranker) can read the query from the
stream instead of needing it passed in.
Building blocks
Step | Shape | Notes |
| 1 → 1 | returning |
| 1 → N |
|
| 1 → 0..1 |
|
| N → n | cancels upstream once full |
| source and sink, used by |
Components
Component | Shape | Status |
| query → N chunks | base class: implement |
| query → N chunks |
|
| filter | drops social and video sites |
Crawler, Chunker, Ranker | chunk → chunk(s) | planned |
Write your own component
Subclass the role and implement one method. Chaining, streaming and concurrency come from the base class.
from abyss import Chunk, Searcher, Take
class NotesSearcher(Searcher):
async def search(self, query: str) -> list[Chunk]:
return [Chunk(url="file:///notes.md", text=f"my notes about {query}")]
notes = NotesSearcher() >> Take(5) # chains like any other stepFor a step that is not a searcher, subclass Map, FlatMap or Filter, or subclass Step and implement
__call__(items) -> AsyncIterator.
Serve steps as MCP tools
Needs the [mcp] extra. Any step or chain becomes a tool that takes one query: str and returns plain
text, one result per block as URL plus text. Input and output stay minimal so the model's context stays
small.
# my_server.py
from abyss import NotBlocked, SearxngSearcher, Settings, Take
from abyss.mcp import MCP
s = Settings() # .env and env vars
web = SearxngSearcher(s.searxng_url) >> NotBlocked() >> Take(s.search_top_k)
papers = SearxngSearcher(s.searxng_url, categories="science") >> Take(3)
mcp = MCP("abyss")
mcp.add(web, name="web_search", description="Search the web. Returns URL and snippet per result.")
mcp.add(papers, name="paper_search", description="Search academic papers. Returns URL and snippet per result.")
if __name__ == "__main__":
mcp.run() # stdioPoint any MCP client at python my_server.py. Pass render= to add to control the output text.
The package also ships a stock server, abyss-mcp, with a single search tool built from Settings.
Settings
abyss.settings.Settings (pydantic-settings, in the [mcp] extra) holds project-level defaults. It reads
.env from the working directory; environment variables win; unknown keys are ignored. Components never
read it: they take constructor arguments, and whatever assembles them reads Settings and passes values in.
Setting | Default | Used by |
|
| search |
|
| search |
|
| search |
|
| search |
|
| crawler (planned) |
|
| chunker, ranker (planned) |
Local serving
SearXNG runs in Docker, llama-server runs on the host in tmux, and llama-server spawns abyss-mcp itself
over stdio, so there is no MCP port.
cp .env.example .env # set SEARXNG_SECRET
uv sync --extra mcp
docker compose up -d # SearXNG
deploy/llm.sh start # llama-server with the web_search toolEverything else, including settings, remote access over Tailscale and troubleshooting, is in docs/DEPLOY.md.
Development
uv sync --extra mcp
uv run pytest # parallel via xdist; -n 0 for a quick serial run
uv run ruff check abyss tests && uv run ruff format abyss testsLicense
MIT
Available Tools
1 toolsearchA
Search the web for current information. Returns the top results as URL and snippet.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It usefully discloses the return shape (URL and snippet), implying a safe read-only operation, but says nothing about result count, ranking, freshness/latency, rate limits, or error behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with zero filler; purpose is front-loaded and the return format follows immediately. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter search tool with no output schema, the description covers purpose and return shape adequately. It stops short of the result-count, ranking, and freshness details an agent would want before deciding how much to trust the output.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the single parameter is undocumented beyond its name. The description adds no query-syntax, length, or operator guidance, though the lone 'query' parameter is largely self-explanatory, keeping this at the minimum-viable level.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Search) and resource (the web) with a scope qualifier (current information), so an agent immediately knows what it does. No sibling tools exist to differentiate from, so it cannot reach the sibling-routing bar of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"For current information" implies the usage context — reach for this when up-to-date web facts are needed — but there is no explicit when-not guidance, no mention of alternatives, and no statement of limits (e.g., real-time vs indexed). Usage is inferred rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v0.1.0- First observed
search
TDQS
Scored across 1 tool
There is only one tool, so there is no possibility of an agent confusing it with another action. Its purpose ('search the web') is unambiguous.
The single name 'search' is a clean, conventional verb choice, but with only one tool there is no pattern to demonstrate consistency against.
A one-tool server is inherently thin; even for a narrow web-search purpose, a single tool leaves no room for query variants, site scoping, or content retrieval.
Search returning only URLs and snippets covers the stated core purpose, but there is no way to fetch or read page contents, which is a common dead end for an agent needing current information.
Maintenance
Related MCP Connectors
Live AI-native web search with citations. One tool for every MCP client. Flat per-request pricing.
LLM-ready web search + instant answers + URL-to-clean-text fetch for agents and RAG.
Agentic search over your Dewey document collections from any MCP-compatible client.
Scrape, crawl and search the web for AI agents via MCP.
Related MCP Servers
- FlicenseAqualityCmaintenanceEnables MCP-compatible agents to perform web searches via the agent-web-search engine, returning ranked results with sources.1-
- AlicenseAqualityCmaintenanceEnables MCP hosts to perform cited web searches and receive source-bearing results with titles, URLs, snippets, and sources for evidence-grounded answers.115 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables remote MCP access to tools for URL-to-markdown conversion, web and image search, academic search, embeddings, reranking, classification, deduplication, and PDF extraction.Apache 2.0
- AlicenseBqualityBmaintenanceEnables AI agents to perform web searches and extract full-text content from web pages via standard MCP tools, with fallback search and semantic reranking.22MIT