google-surf-mcp
This server provides Google search and web content extraction without requiring an API key, using a persistent Chrome profile with stealth capabilities.
search(query, limit?)– Run a single Google search and get title, URL, and snippet for up to 20 results.search_parallel(queries[], limit?)– Execute up to 10 queries simultaneously using a pool of 4 concurrent workers, returning title/URL/snippet per result.extract(url, max_chars?)– Fetch a URL and return clean article content as Markdown (via Mozilla Readability), with optional truncation up to 50,000 characters.search_extract(query, limit?, max_chars?)– Combines search and extraction in one call: performs a Google search and extracts full article Markdown from each result page in parallel (up to 10 results, up to 20,000 chars each), replacing the typical two-step search + fetch workflow.
Key highlights:
No API key, proxies, or external solvers required
Automatic CAPTCHA recovery: opens a visible Chrome window for a human to solve, then retries automatically
Blocks images/media/fonts for faster performance
Designed for local use with a persistent, warm Chrome profile
Provides tools for searching Google and extracting web content without API keys, including search, parallel search, URL extraction, and combined search+extract.
google-surf-mcp
English | Korean
Web searches, papers, and GitHub repositories are stored as PKM, ontology, and lineage. The view above is generated with
project_memory(action="export", export_format="html", export_view="graph", all_projects=true).
"Turn Google Search, Papers, and Codebases into an Automatic Local Knowledge Graph and lineage for AI Agents with Zero API Key, Zero External Server."
Google Surf stores search and extraction results in a project-scoped local knowledge graph.
As you search, papers, code, web sources, session intent, plans, experiments, and decisions accumulate in a personal PKM. New research searches stored knowledge and fresh web results together, reducing repeated work while continuing to discover new information.
Projects remain isolated by default. Only verifiable links such as matching DOIs, repository URLs, or explicit aliases are added, so knowledge from one project can be reused in another without merging the original records.
Retrieval runs exact search, BM25, vector search, code and graph search, and live web independently, then combines them with RRF and one shared reranker.
Live web + Papers + Codebases + Project memory
↓
Exact + BM25 + Vector + Code graph + Graph PPR
↓
RRF + Shared reranker
↓
Results with evidence and provenanceSeven tools are available by default: search / search_parallel / extract / scholar_search / project_memory_search / project_memory / health.
Research mode and automatic capture are enabled by default. Set SURF_RESEARCH=false to use search and extraction without opening the database or graph sidecar; the project memory tools are not registered in that mode.
With research mode disabled, live search and search_parallel results still use a lightweight in-memory reranker. It fuses the provider order with query BM25 rank through RRF without loading the vector model or opening local storage.
Browser search needs no API key. SearchApi can be configured as an optional primary provider or fallback.
Core features
Web, paper, and codebase search: Google web search is the default, while Scholar is used for paper-specific metadata. SearchApi can act as an optional primary provider or fallback.
Web and academic document extraction: HTML and PDF extraction returns available titles, authors, DOIs, publication metadata, and body text.
searchandsearch_parallelcan include abstracts or full bodies.Automatic project memory: Search results, extracted bodies, and code repositories are stored in the current project. Unread results remain metadata; extracted content becomes active RAG evidence.
Structured codebase search: Tree-sitter links files, symbols, imports, and calls from local projects and relevant GitHub repositories. Exact, BM25, vector, and graph search retrieve the code.
Graph hybrid retrieval: Fresh web results, papers, stored content, codebases, and the project graph are searched independently. Exact, BM25, vector, and PPR candidates are combined through RRF and one shared reranker.
Ontology and data lineage: Web sources, papers, code, plans, experiments, and decisions become typed entities and relations. Evidence paths run from source through documents, chunks, symbols, evidence, and assertions.
Cross-project knowledge reuse: Projects remain isolated but selected projects can be searched together. Only verifiable links connect matching entities.
Durable research history: Session intent, plans, experiments, failures, and decisions supplied by the MCP host are stored as revisions and linked to their supporting evidence.
Local graph analysis and export: PageRank, PPR, connected components, and Louvain communities run without a separate Neo4j server. Results export to HTML, Graphviz, D3 JSON, and Neo4j import formats.
Related MCP server: G-Search MCP
Search
API-key-free system Chrome search
Dedicated logged-out profile that never reads or copies the user's Chrome profile
Multi-strategy SERP parsing with geometric verification
Sponsored block and knowledge panel removal
CAPTCHA detection and environment-specific recovery
Parser self-healing and context fallback
Numbers
Search speed
result | |
search | 4.0-5.1s/query |
scholar_search | 3.8-5.6s/query |
Live web search reranker check
32 held-out queries | nDCG@5 | MRR | Precision@5 |
Provider order | 0.8949 | 0.8203 | 0.6375 |
BM25 + RRF | 0.8971 | 0.8203 | 0.6500 |
web search responses with BM25+RRF reduce large retrieval payloads by up to 75%. Measured across three uncached queries per provider on a workstation with a 1Gb/s connection. Network and Google response time vary.
Tech Stack
Runtime: Node.js, TypeScript, Model Context Protocol SDK
Web search: System Chrome + CDP, Playwright compatibility fallback, SearchApi fallback
Web extraction: Mozilla Readability, Turndown
PDF extraction: LiteParse/PDFium, optional OCR,
pdf-libmetadata parsingCode collection: Local project roots and gated GitHub sparse download
Code parsing: Tree-sitter for files, symbols, imports, and call relations
Code search: Exact lookup, BM25, Multilingual E5 vector search, and graph PPR
Local database: Embedded SurrealDB on RocksDB
Hybrid retrieval: Live web, papers, project memory, and codebase results combined through RRF
Ranking: Reciprocal Rank Fusion and shared vector reranking
Graph analysis: Graphology, PageRank, PPR, connected components, and Louvain communities
Knowledge model: Versioned ontology, data lineage, cross-project schema and entity linking
Recovery: CAPTCHA recovery, Playwright pool fallback, and deterministic parser self-healing
Install
Requires Node 20.18.1+. Browser mode also requires Google Chrome or Chromium.
npx google-surf-mcp # actual MCP - register in client configFirst tool call auto-bootstraps the warm profile (you may see Chrome open briefly).
Or local clone:
git clone https://github.com/HarimxChoi/google-surf-mcp
cd google-surf-mcp
npm installIf auto-bootstrap fails (rare), run it manually:
npm run bootstrapOverride paths if needed:
CHROME_PATH=/path/to/chrome SURF_TZ=America/New_York npm run bootstrapOptional Codex output protection
Google Surf can install an opt-in Codex hook that ranks oversized Bash output before it reaches the model. The host still executes the command; the hook only applies stateless source-order, exact, and BM25 ranking with RRF. It opens neither the Google Surf database nor a browser and does not store command output.
npx -y google-surf-mcp@latest hooks install --host codexRestart Codex, then open /hooks to review and trust the definitions. Shell output is reranked from 1,500 characters, with a default 1,500-character display budget that expands up to 3,000 for distinct query-matching evidence records or blocks. An explicit override may exceed 3,000; there is no cumulative per-turn output limit. Two identical or near-duplicate searches and explicit foreground polling loops are blocked. Existing write_stdin polls for an already-running unified command remain controlled by the Codex runtime, not this hook.
JSON/JSONL summaries use compact records with input-relative JSON pointers, ancestor identity/condition references and original result values. Markdown table rows retain headers and adjacent context, including source lines. Selected records and their required context must fit together; the renderer reports omissions instead of slicing records. Plain logs retain block reranking. Selection is deterministic and lexical, without a model, database or extra subprocess. Context extraction uses structural/field-name heuristics, not semantic understanding; unselected fields remain in the original input. These excerpts do not replace experiment logs or SSOT history. Input-relative pointers are not persistent retrieval handles. Existing installed hook bundles require an update to use changed code.
npx -y google-surf-mcp@latest hooks status --host codex
npx -y google-surf-mcp@latest hooks update --host codex
npx -y google-surf-mcp@latest hooks uninstall --host codexUse with Claude Code
Paste this into your ~/.claude.json:
{
"mcpServers": {
"google-surf": {
"command": "npx",
"args": ["-y", "google-surf-mcp"]
}
}
}Restart Claude Code. All seven tools, including project_memory_search and project_memory, are available by default.
For other MCP clients, use the same JSON shape in their config file.
Search providers
Browser search remains the default. SearchApi can be selected as the primary provider or used only when browser search fails.
value | behavior |
| Default. Uses system Chrome with a dedicated logged-out profile, keeps native search windows hidden, and does not require |
| Uses SearchApi as the primary provider and does not initialize Chrome for that tool. |
| Tries the current browser tier once, then uses SearchApi on browser errors, CAPTCHA/rate limits, profile failure, or parser degradation. It does not wait for human CAPTCHA recovery. Successful and normal empty browser responses are not repeated. |
SURF_SEARCH_PROVIDER controls search and search_parallel. SURF_SCHOLAR_PROVIDER controls scholar_search. SearchApi modes require your own SearchApi account, key, and available credits.
SURF_BROWSER_ENGINE=auto selects native Chrome on a local desktop and the Playwright compatibility path in cloud or remote-debug mode. Native mode uses a normal hidden Chrome window, not headless Chrome. Set native or playwright to pin the engine.
{
"mcpServers": {
"google-surf": {
"command": "npx",
"args": ["-y", "google-surf-mcp"],
"env": {
"SEARCH_API": "your-searchapi-key",
"SURF_SEARCH_PROVIDER": "fallback",
"SURF_SCHOLAR_PROVIDER": "searchapi"
}
}
}
}Local clone variant:
{
"mcpServers": {
"google-surf": {
"command": "node",
"args": ["/abs/path/to/google-surf-mcp/build/index.js"]
}
}
}Tools
search(query, limit?, extract_mode?, extract_limit?, response_content?, max_chars?)- primary single-query tool for live discovery and reading. When new sources must be found and read, setextract_modein this call instead of downloading PDFs, cloning repositories, or callingextractseparately. Useextractonly when the exact public URL is already known and no discovery is needed. Withproject_id, stored project knowledge is fused with live results, but the call never becomes local-only.limitis 1-20. Extraction defaults tonone;extract_limitis 1-10 with default 5.response_contentdefaults tosummaryto bound one-call output.scholar_search(query, limit?)- Google Scholar metadata search, max 10 papers. Supports browser, SearchApi primary, and fallback modes.search_parallel(queries[], limit?, extract_mode?, extract_limit?, response_content?, max_chars?)- primary multi-query tool for broad live discovery and reading through a continuous four-tab queue. Setextract_modein the same call when public web pages, PDFs, papers, or GitHub repositories must be read. Use local PDF tools only for local files or visual layout work, and clone repositories only for editing, building, testing, or full Git history.limitis 1-20 per query. The call-wideextract_limitdefaults to 12 and allows up to 20 for abstract; full defaults to and allows 10.response_contentdefaults tosummaryto bound one-call output.Integrated search extraction reports
requested,applied,skipped,truncated, andtotal_chars.remaining_urlscan be passed toextractwithout repeating the search.extract(url, max_chars?, mode?, response_content?)- secondary extraction tool for an exact public URL when no new discovery is required. If sources still need to be found, usesearchorsearch_parallelwithextract_modeinstead.mode="full"(default): reads up to 1000000 characters for research capture. Research mode stores deterministic 4000-character chunks;response_content="full"returns up to 50000 characters andsummaryreturns a 1500-character evidence excerpt.mode="abstract": ~1500-char survey (PDF page 1 or HTML meta description). Document metadata is included and stored with the survey when research mode is enabled.mode="metadata": metadata without body text. Returns available title, authors, publication, dates, DOI, description, keywords, canonical URL, and PDF properties including page count.GitHub repository URLs read the README in metadata mode. Abstract and full use the same download gate and differ only in indexed source depth.
Response: content fields plus available document metadata. Failures return
{ error }, never throw.
project_memory_search(query, query_variants?, project_id?, include_project_ids?, all_projects?, limit?, request_id?, response_deadline_ms?)- searches stored local knowledge only. Up to 19 optional variants run inside one broker request with batched query embeddings, RRF fusion, evidence-seeded graph expansion, and one final rerank againstquery. The response contains only bounded query-focused summaries from the final ranking; stored bodies stay in the database. A caller-suppliedrequest_idmakes a long query observable and cooperatively cancellable throughproject_memory. If a deadline arrives after retrieval has produced candidates, the completed lanes return as an explicitly partial result instead of being discarded. It never opens a browser or calls Google or SearchApi.project_memory(action, ...)- manages durable project knowledge whenSURF_RESEARCH=true.action="search": compatibility alias forproject_memory_search.action="project_update": updates an existing project name, purpose, constraints, or protected parents as a revision-checked profile revision.action="context": separates the durable project purpose, confirmed session intent, provisional recent query, current plan, scientific experiment state, and observed process liveness.action="get": returns exact typed or legacy records through bounded UTF-8 byte spans. Setbody_bytes=0for metadata only and usenext_body_offsetfor lossless paging.action="show": always returns a bounded summary with counts and active record IDs.detail_level="full"is accepted for compatibility but never dumps every record body. Useproject_memory_searchfor relevant bodies ortarget_idfor one assertion or entity.action="record": stores the submitted body and returns only its ID, revision, and status. Receipts distinguish the original application from an idempotent replay.action="query_status"/action="query_cancel": inspects or cooperatively stops a local retrieval identified before execution byrequest_id.action="export": writes a standalone interactive HTML explorer, Graphviz DOT, D3 node-link JSON, or a Neo4j import bundle under<research-root>/exports.
health()- server status, including the local research runtime.
Need | Tool |
Search only previously stored research and project memory |
|
Find new information on the web |
|
Compare new web results with stored project knowledge |
|
Run several new web queries |
|
Replayable research collection
google-surf-collect runs a versioned JSON specification through one persistent MCP session. A specification can mix live search jobs with local-only project_memory_search jobs. Live jobs can extract bodies in the same call, while local jobs reuse indexed project knowledge without opening Google.
npx google-surf-collect examples/research-collection.example.jsonFrom a source checkout:
npm run build
npm run research:collect -- examples/research-collection.example.jsonA project workflow can also record durable sessions and plans, rebuild approved code roots, search the resulting local knowledge, and export its graph:
npm run research:collect -- examples/project-memory-workflow.example.jsonproject_memory collection jobs allow record, rebuild, and export. Destructive forget operations are not accepted by the collection schema. Project-level project_id is inherited by every job unless an all-project export is requested.
The output is append-only JSONL. Its manifest records the normalized specification hash, package version, Git commit, Node runtime, platform, project setup, and server health. Every search, record, rebuild, and export result records the stable job id, exact tool arguments, attempt, timestamps, elapsed time, response, and error state. Successful jobs are skipped on resume; failed jobs are retried. A changed specification requires a new output file. Set project_name with project_id when the runner should create a missing project; existing projects are reused.
Set retrieval_mode to live when prior project RAG state must not affect live result ranking. Results are still captured under project_id. Use hybrid when the collection intentionally ranks new web evidence together with stored project knowledge. API keys and environment variable values are never written to the collection log.
This makes the collection procedure and returned snapshot replayable and auditable. Live web results can still change with time, locale, network route, and upstream ranking.
Graph hybrid RAG with ontology and lineage
flowchart TB
subgraph SOURCES["1. Search and research"]
direction LR
LIVE["Live web<br/>Google browser • SearchApi fallback"]
PAPER["Pages and papers<br/>extract • Scholar metadata"]
PROJECT_INPUT["Code and project records<br/>local roots • GitHub • host-provided session/plan"]
end
INGEST["2. Deterministic capture<br/>normalize • deduplicate • content hash<br/>repository source gate • Tree-sitter"]
subgraph KNOWLEDGE_BASE["3. SurrealDB knowledge base"]
direction LR
CONTENT["Content and code index<br/>exact • BM25 • HNSW vector<br/>documents • chunks • symbols"]
PROV["Data lineage and provenance<br/>source → evidence → assertion<br/>valid time • recorded time • correction"]
ONTOLOGY["Versioned ontology<br/>core/project term revisions<br/>entity type • relation • alias • merge/split"]
MEMORY["Project memory<br/>session intent • plan revisions<br/>experiments • decisions"]
end
subgraph INTELLIGENCE["4. Graph intelligence"]
direction LR
SCHEMA["Cross-project schema linking<br/>type and relation alignment<br/>stable identifier → identity bridge"]
SIDECAR["Typed graph sidecar<br/>PageRank • Louvain • query-time PPR"]
end
FUSION["5. Hybrid retrieval<br/>live • exact • BM25 • vector • graph<br/>deterministic RRF • shared reranker • fresh-web floor"]
RESULTS["Results + provenance<br/>short storage receipt"]
LIVE --> INGEST
PAPER --> INGEST
PROJECT_INPUT --> INGEST
INGEST --> CONTENT
INGEST --> PROV
INGEST --> ONTOLOGY
INGEST --> MEMORY
ONTOLOGY --> SCHEMA
CONTENT --> SIDECAR
PROV --> SIDECAR
MEMORY --> SIDECAR
SCHEMA --> SIDECAR
LIVE --> FUSION
CONTENT --> FUSION
SIDECAR --> FUSION
FUSION --> RESULTS
RESULTS -. "automatic search/extract capture" .-> INGEST
classDef inputStyle fill:#eef6ff,stroke:#2563eb,color:#172554
classDef processStyle fill:#fff7ed,stroke:#ea580c,color:#431407
classDef storageStyle fill:#ecfdf5,stroke:#059669,color:#052e16
classDef intelligenceStyle fill:#f5f3ff,stroke:#7c3aed,color:#2e1065
classDef outputStyle fill:#f8fafc,stroke:#475569,color:#0f172a
class LIVE,PAPER,PROJECT_INPUT inputStyle
class INGEST processStyle
class CONTENT,PROV,ONTOLOGY,MEMORY storageStyle
class SCHEMA,SIDECAR intelligenceStyle
class FUSION,RESULTS outputStyleOne local knowledge base
One SurrealDB instance is the authority for three distinct layers:
Catalog: stable identities, revisions, project membership, ontology, provenance, and temporal links.
Payloads: exact bodies, source snapshots, manifests, and artifact references. Identical content is stored once and can be fetched by ID and byte span.
Derived views: exact, BM25, vector, code, and graph indexes plus the compact current-project view. These are versioned by source hash and can be rebuilt.
A single local research broker owns the embedded RocksDB connection. Multiple MCP sessions connect through authenticated local IPC, run bounded reads concurrently, and serialize writes without opening another database process. Original evidence and historical occurrences remain addressable even when bodies, embeddings, or graph structures are deduplicated.
Durable records and incremental indexing
project_memory writes project profiles, plans, experiments, attempts, measurements, artifacts, documents, decisions, and sessions as typed records. A stable asset ID receives revisions; an uncertain retry with the same idempotency key returns the committed revision with idempotent_replay=true and applied_this_request=false. body_path streams bodies up to 256 MiB, artifact manifests are paginated instead of sliced, and get/get_batch return exact typed or legacy records through bounded body spans. Compact write receipts report accepted bytes, references, revision, and the exact readback handle without echoing the submitted body.
sync accepts registered roots or explicit changed and removed paths. Unchanged roots and parent identities remain intact, a no-op does not rewrite derived indexes, and a one-file change publishes only the affected generation. Code, retrieval, and graph publication run under one durable job ID. job_wait uses a revision cursor, job_cancel stops at a safe stage boundary, and unfinished jobs resume after process restart. Under the worker soft-memory limit, foreground requests finish first; rebuildable index jobs checkpoint at a durable stage boundary and resume in the replacement worker without cancelling the committed record.
Ontology and cross-project links
The versioned ontology preserves entity type and relation changes as revisions. Schema linking aligns project-specific types and relations with the shared schema. Entity linking connects the same paper, repository, or entity only when verifiable identifiers such as a DOI, repository URL, or explicit alias match. Ambiguous candidates are not linked automatically.
Data and research lineage
Source lineage:
source → document → chunk → evidence → assertionCode lineage:
repository → directory → file → symbol → import/callResearch lineage:
session → intent → plan revision → experiment → decision
This preserves the evidence behind claims and decisions while retaining corrected or superseded history.
Graph retrieval
Graphology builds a typed graph projection from SurrealDB and computes PageRank, connected components, and Louvain communities. At retrieval time, related nodes seed PPR-based multi-hop search. Live web, exact, BM25, vector, and graph candidates are combined through deterministic RRF and one shared reranker.
Local multi-query retrieval batches query embeddings, keeps exact, BM25, and vector lanes independent through RRF, expands the graph once, hydrates selected chunks once, and reranks once. Graph-only all-project searches use a lightweight memory-node index and verified identity aliases to select at most four graph scopes instead of constructing every project graph at query time.
If query embedding fails or returns no vector, local search returns its lexical/graph ranking without retrying the model in reranking. Timing fields are non-overlapping wall-clock partitions: embedding_ms plus retrieval_ms reaches the end of retrieval, and rerank_ms covers the remaining response work. Retrieval may overlap embedding, so retrieval_ms is not standalone database execution time. A supplied response_deadline_ms starts after broker admission; the transport watchdog retains its five-minute queue/IPC allowance in addition to that deadline. Native queries that outlive a partial response retain their broker read slot until they settle.
Research retrieval check
The 60-query regression fixture compares the lexical baseline with the complete local RAG path. To separate retrieval changes from HNSW approximation, the check pins one embedding thread and uses exhaustive vector scoring. The table reports the median of three fresh-process runs:
60 controlled queries | Recall@10 | nDCG@10 | MRR@10 |
Lexical baseline | 0.3333 | 0.3333 | 0.3333 |
Exact + BM25 + Vector + Graph RRF | 0.9833 | 0.8194 | 0.7668 |
+ shared reranker | 0.9833 | 0.8194 | 0.7668 |
All retrieved targets passed exact provenance readback. The conservative reranker preserved the fused order on this fixture; this check does not claim production search quality or a reranker gain.
npm run research:vector-backends -- --rows 5000 compares physical representations with deterministic test vectors. The sample below excludes E5 model loading and should be rerun on the target machine:
Backend | Input rows | Stored vectors | Insert | Query p95 | Recall |
HNSW | 5,000 | 5,000 | 1292.1 ms | 10.77 ms | 1.000 |
Compact | 5,000 | 1,250 | 357.1 ms | 3.96 ms | 1.000 |
Exhaustive | 5,000 | 5,000 | 1197.1 ms | 289.79 ms | 1.000 |
The default remains HNSW for full-chunk semantic coverage. Compact indexes one representative chunk per source; it used less memory and matched aggregate recall in this short-record fixture, but can miss rare details near the tail of long sources.
Broker and durability check
Latest 100-query local soak | Result |
Browser broker launches | 0 |
Active database owner restarts | 0 |
Query RSS delta | +10.9 MiB |
Peak observed research worker RSS | 375.5 MiB |
Idle drain and transparent reopen | Passed |
No-op sync / one-file incremental sync | Passed / Passed |
npm run research:reopen-probe also verifies byte-exact body paging, a paginated 225-reference artifact manifest, and a pending rebuild that resumes after process restart and becomes searchable. The soak runs with the vector model disabled to isolate broker, database, and query lifecycle memory; vector backends are measured separately above.
Project isolation and knowledge reuse
project_id selects where new results are stored. include_project_ids expands the read scope without changing the write target. Original records remain isolated by project, while verified schema and entity links allow papers, code, and experiment results to be reused across selected projects.
Interactive graph and export
Use project_memory(action="export", export_format="html", export_view="graph"). The returned standalone HTML opens locally without a server and contains three coordinated views. Use project_id for one project, include_project_ids for a selected combined graph, or all_projects=true for every project.
PKM groups the integrated project graph by community and sizes nodes by PageRank.
Lineage separates source and code lineage from session, intent, plan, experiment, and decision lineage while keeping both flows aligned by stage.
Ontology shows core types and relations, aligned shared schema, and typed instances. A verified identity layer appears only when stable identifiers or explicit aliases prove a cross-project match.
Search, type filters, one to three hop local focus, pan, zoom, and the provenance inspector work inside the file. Large graphs use a deterministic semantic projection that balances node type, PageRank, degree, and community coverage. The viewer reports source and displayed counts, replaces internal IDs with local aliases, disambiguates repeated labels, and does not embed source IDs, local paths, node bodies, plan text, or evidence quotes.
The project menu switches between every project embedded in the export and an integrated All projects view. Use all_projects=true when exporting to include every named project in the local database, or use project_id and include_project_ids for a bounded set. Clicking empty canvas space clears local node focus. PNG exports the current canvas, while JSON exports the current tab, project, type filters, and local focus using only the anonymized viewer payload. The standalone file has a nonce-bound script CSP, makes no network connections, and permits source links only for stripped public HTTP or HTTPS URLs.
Neo4j export
Use project_memory(action="export", export_format="neo4j", export_view="graph"). The returned directory contains nodes.csv, relationships.csv, constraints.cypher, load.cypher, manifest.json, and README.txt. PageRank, community, ontology, lineage, project IDs, source IDs, and evidence IDs are preserved. Node bodies, plan text, and evidence quotes are not exported.
For a new or empty local database, run the Neo4j offline importer from the export directory:
neo4j-admin database import full --nodes=nodes.csv --relationships=relationships.csv neo4jFor an existing local database, copy both CSV files to the Neo4j import directory, then run:
cypher-shell -f constraints.cypher
cypher-shell -f load.cypherThe offline importer creates typed node labels and relationship types. The online loader uses SurfNode and SURF_RELATION, retaining the original kinds and relationship types as properties. neo4j-admin database import full is intended for a new or empty database; use LOAD CSV for an existing database. See the official Neo4j import and LOAD CSV documentation. Bolt is a connection protocol, not an export file format, so this command does not connect to or modify a Neo4j server.
Storage scope and security
Research mode is enabled by default. search, search_parallel, scholar_search, and extract results are captured automatically. Session intent, plans, experiments, and decisions are stored only when the MCP host sends them through project_memory; versioning, ontology mapping, and lineage linking then run automatically. Retrieval mode is server configuration, not a per-call argument.
Image retrieval, image embeddings, and visual reranking are not part of research memory. OCR is used only to recover searchable text from scanned PDF pages.
Credential and private-key files are excluded from body indexing. HTML exports omit source IDs, local paths, node bodies, plan text, and evidence quotes. The viewer initiates no network requests and opens stripped public HTTP or HTTPS source links only after user action.
Project and assertion deletion require a count preview and confirmation token. They create reversible tombstones and preserve evidence and correction history. Fact correction takes only target_id, replacement, and reason; the prior assertion remains as bitemporal history. Plan revisions are append-only. Experiments are bound to the active revision and must be finished explicitly as success, failed, or inconclusive. Logs are not used to infer an outcome. Receipts list stored categories only:
Project: Graph memory | Session: temporal graph research | Stored: paper 1 (Graphiti), repo 1 (getzep), search summaries 3 | Status: readySet SURF_RESEARCH=false to keep the database and sidecar closed and omit project_memory_search and project_memory. Obsidian and Notion sync are not included and will remain project-level opt-in when added.
Local operations CLI
These commands use the same authenticated research broker as MCP sessions. They do not open another embedded database owner or browser:
npx google-surf-mcp doctor --json
npx google-surf-mcp daemon start --background --no-window
npx google-surf-mcp memory get --project PROJECT --record RECORD --json
npx google-surf-mcp memory sync --project PROJECT --manifest changes.json --json
npx google-surf-mcp memory current --project PROJECT --json
npx google-surf-mcp job wait --project PROJECT --id JOB --after-revision 0 --json
npx google-surf-mcp job cancel --project PROJECT --id JOB --reason "operator request" --json
npx google-surf-mcp daemon drain --jsondoctor is passive: it reports process identity, versions, capabilities, queue counters, memory counters, cache bytes, current lifecycle state, foreground/background ownership, checkpoint-drain state, and the exact blockers preventing worker recycling without opening the database, vector model, or graph. The broker drains after meaningful work is idle even if an MCP client remains connected; active requests and commits postpone the drain, while rebuildable background jobs checkpoint at safe stage boundaries under memory pressure. The next request reconnects transparently.
Env vars
var | default | notes |
| unset | SearchApi API key. Required only when either provider setting is |
| unset | Alias for |
|
| Provider for |
|
| Provider for |
|
| Browser engine: |
| auto-detected | absolute path to Chrome binary |
|
| where the warm profile lives |
|
| enables local project memory, capture, indexing, |
|
| research search route: |
|
| embedded SurrealDB data directory |
| unset | optional remote SurrealDB endpoint; requires |
|
| SurrealDB namespace used by the research catalog |
|
| SurrealDB database used by the research catalog |
| unset | bearer token for a remote research database; alternatively set |
|
| local 384-dimensional model used by HNSW vector retrieval and final reranking. The default model revision is pinned; |
|
| disables the ONNX CPU memory arena and memory pattern; set |
|
| ONNX intra-op thread count, clamped to 1-16 |
|
| vector representation: |
|
| automatically sparse-index at most one small, relevant GitHub repository per search call |
|
| maximum searchable source-text size for automatic GitHub indexing; assets are excluded |
|
| maximum searchable source file count for automatic GitHub indexing |
|
| drain delay after the last meaningful work, even when clients remain connected; active requests and background jobs postpone drain |
|
| maximum concurrent broker reads; identical in-flight reads share one operation |
|
| timeout per embedded SurrealDB query, clamped to 1-600 seconds; a timed-out retrieval lane is reported as partial while other lanes still return |
|
| byte budget for in-process graph projections and analysis artifacts |
|
| RSS threshold that requests an immediate idle drain after active work completes |
|
| shell-output interception threshold (inclusive); sensitive output is sanitized even below this threshold |
| adaptive: 1500–3000 | optional explicit shell-summary limit, including values above 3000; default is |
| unset | optional GitHub token that raises API limits for repository inspection |
| auto, max 4 | Tree-sitter worker count for initial code structure indexing |
|
| browser locale |
| system tz | e.g. |
|
| Controls Playwright extraction, compatibility, and recovery paths. Native search keeps a normal system Chrome window hidden and shows it only for CAPTCHA recovery. |
|
| set |
|
| lifetime of the background human-recovery window. MCP calls return immediately and do not wait for this timeout. |
|
| idle ms before closing the sequential ctx and pool. |
|
| set |
|
| full extraction limit (200-50000); abstract defaults to 1500 and per-call |
|
| OCR scanned/image PDFs via Tesseract (slower; off by default) |
|
| headless/serverless mode: TLS bypass + |
|
| pin a single stealth mode (chosen by |
|
| initial stealth tier; only consulted when |
|
|
|
|
| internal cap on Google-facing requests per minute |
|
| search cache TTL (24h); |
|
| LRU cap per cache namespace |
|
| cache directory |
|
|
|
|
|
|
|
| set |
|
| directory for jsonl telemetry files. UTC-dated one file per day ( |
|
| per-strategy outcome tracking + persisted reordering. Healing must win by 3 outcomes before reorder kicks in, so single-call flapping is impossible. Set |
|
| persistence path for healing state. Atomic tmp+rename writes; debounced 5s. |
|
| opt-in for LLM-assisted selector repair in the workflow-only |
|
| LLM repair provider: |
| provider default | model for LLM-assisted repair. Defaults to |
| unset | Anthropic key used only when LLM repair is enabled with the Anthropic provider. |
| unset | OrcaRouter key used only when LLM repair is enabled with the OrcaRouter provider. |
| unset | Alias for |
OrcaRouter
SURF_LLM_HEAL=true
SURF_LLM_PROVIDER=orcarouter
ORCAROUTER_API_KEY=...
SURF_LLM_MODEL=orcarouter/autoTroubleshooting
Native search keeps the current session open and shows Chrome when a CAPTCHA appears. Solve it in that window and retry; the next call verifies the page, restores the hidden window guard, and continues with the same session. SearchApi fallback remains available through
SURF_SEARCH_PROVIDER=fallbackandSURF_SCHOLAR_PROVIDER=fallback.Playwright CAPTCHA recovery has 4 modes (picked automatically from env):
default (local desktop): OS notification fires, headed Chrome opens, and the call returns; solve it and retry
SURF_HEADLESS=false: headed Chrome opens without a notification; solve it and retrySURF_REMOTE_DEBUG=true: DevTools port + instructions printed, attachchrome://inspectlocally to solveSURF_CLOUD_MODE=true: fail-fast withCAPTCHA_REQUIREDerror
Headed Chrome opens to a plain search box instead of CAPTCHA: just type any query in the box and press Enter. Subsequent calls work.
"Chrome not found": install Chrome or set
CHROME_PATH.Stale selectors: runtime per-strategy reorder (
SURF_SELF_HEALING, deterministic) plus a manually dispatched repair workflow (SURF_LLM_HEALoptional, human review required, never auto-merged).Playwright searches feel slower than expected: check
health().pool.fallback.truemeans the worker pool is using a single context. Native search uses one authenticated local browser broker across MCP sessions. The broker keeps one hidden Chrome process with up to four reusable tabs forsearch,search_parallel, andscholar_search. Query starts are staggered. A CAPTCHA shows and preserves that session for user recovery, then the window guard is restored on the next call. A browser crash starts a new session on the next call.SSRF:
extractblockslocalhost, private IPs, AWS metadata by default. SetSURF_ALLOW_PRIVATE=trueto allow them.Cache cleanup:
npm run cache:clearremoves search/extract and downloaded vector-model caches. It does not remove the research DB.The local research DB is not application-encrypted. Use OS account permissions and disk encryption such as BitLocker or FileVault when the machine or backups need at-rest protection.
Changelog
See CHANGELOG.md.
License
MIT
Available Tools
7 toolsextractKnown URL ExtractA
SECONDARY KNOWN-URL CONTENT EXTRACTION TOOL. Use only when the exact public URL is already known and no new web discovery is required. If sources still need to be found, use search or search_parallel with extract_mode instead. For semantic reading of a public PDF or GitHub repository, use this tool before local download or parsing. Local PDF tools are for local files, forms, OCR recovery, or visual layout inspection; general repository tools are for editing, building, testing, or full Git history. Fetch one public URL and return clean content. With research enabled, one local broker lets multiple MCP sessions query the same knowledge base concurrently and orders writes safely. For GitHub repository URLs, metadata reads the README; abstract and full use the same bounded download gate and differ only in indexed source depth. HTML via Mozilla Readability; academic PDFs (arxiv/biorxiv/Nature/OpenReview/NeurIPS/JMLR/PMLR/Springer/PubMed-via-PMC) auto-detected via Content-Type, %PDF magic, citation_pdf_url meta, and per-domain URL rules. Tiered depth: mode="metadata" returns document metadata without body text, mode="abstract" returns about 1500 chars for relevance checks, and mode="full" reads the full bounded source. response_content controls whether the response contains a 1500-character summary or up to 50000 characters. PDF and landing-page metadata are merged when available. With research enabled, abstract and full PDF reads are stored as searchable evidence with bibliographic metadata and provenance. Best-effort: failures return an errorInfo instead of throwing.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Public http(s) URL. Loopback/private IPs blocked unless SURF_ALLOW_PRIVATE=true. | |
| mode | No | Extraction depth. `full` = whole article body (default; uses Playwright if needed). `abstract` = cheap survey: PDF page 1 OR HTML meta description (~1500 chars); use to triage relevance before paying for full text. `metadata` = document metadata without body text: title, authors, publication details, dates, DOI, keywords, canonical URL, and PDF properties when available. Academic PDFs (arxiv/biorxiv/Nature/OpenReview/NeurIPS/JMLR/PMLR/Springer/PubMed-via-PMC) are auto-detected; abstract mode skips Playwright for them. | full |
| max_chars | No | Maximum returned characters when response_content=full. Defaults to 1500 for abstract and 50000 for full. | |
| project_id | No | Project memory id. | |
| session_id | No | Stable host task id. Reuses the same project session after restart. | |
| memory_handle | No | Reuse the handle returned by a prior project-aware call. | |
| session_intent | No | Current durable task intent. A changed value creates an immutable revision. | |
| response_content | No | Controls only the returned body. full returns up to max_chars; summary returns a 1500-character evidence excerpt. Research storage keeps the full captured text in deterministic chunks. | full |
Output Schema
| Name | Required | Description |
|---|---|---|
| doi | No | |
| url | No | |
| meta | No | |
| year | No | |
| error | No | |
| title | No | |
| is_pdf | No | |
| length | No | |
| memory | No | |
| authors | No | |
| content | No | |
| creator | No | |
| excerpt | No | |
| subject | No | |
| keywords | No | |
| language | No | |
| producer | No | |
| truncated | No | |
| created_at | No | |
| elapsed_ms | No | |
| page_count | No | |
| description | No | |
| modified_at | No | |
| publication | No | |
| published_at | No | |
| canonical_url | No | |
| memory_handle | No | |
| source_length | No | |
| extraction_quality | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Despite annotations already providing some signals, the description adds substantial behavioral detail: best-effort failures return errorInfo instead of throwing, PDF and GitHub URL handling are explained, research-enabled memory writes are disclosed, and mode/response_content behavior is specified. This goes well beyond the annotation surface and gives the agent a realistic model of side effects and failure 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?
The description is front-loaded with purpose and usage, then organized into behavior, modes, and failure semantics. It is dense but mostly relevant; a few sentences, such as the local broker concurrency note, feel tangential and partly overlap with what the schema already conveys. This prevents a 5 but is still well-structured.
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 tool with 8 parameters, an output schema, and meaningful side-effect behavior, the description is complete: it covers when to use it, when not to, mode semantics, output size controls, PDF and GitHub behavior, memory/research implications, and error handling. Nothing critical for correct invocation or selection is missing.
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 coverage is 100%, so the baseline is 3, but the description adds real meaning beyond the schema: it explains what each mode returns, how response_content interacts with max_chars, how PDF auto-detection works, and how memory/research parameters relate to stored evidence. It does not substantially clarify project_id, session_id, memory_handle, or session_intent beyond the schema, so a 4 is appropriate rather than 5.
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?
The description opens with 'SECONDARY KNOWN-URL CONTENT EXTRACTION TOOL' and states 'Fetch one public URL and return clean content,' giving a specific verb, resource, and scope. It also explicitly distinguishes this tool from search and search_parallel, so an agent can tell it apart from siblings without guessing.
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?
Usage guidance is explicit and actionable: 'Use only when the exact public URL is already known and no new web discovery is required.' It names alternatives with conditions ('use search or search_parallel with extract_mode instead') and even clarifies when local PDF and repository tools should be preferred. This leaves little room for misrouting.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
healthMCP Health CheckARead-onlyIdempotent
MCP server status: cascade mode + transitions, rate-limiter usage, cache size, config. Call this if searches start failing or returning empty -- check cascade.totalCaptchas and rateLimiter.queueSize, and reduce search volume if they are high.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| pool | No | |
| cache | No | |
| error | No | |
| config | No | |
| cascade | No | |
| version | No | |
| research | No | |
| telemetry | No | |
| rateLimiter | No | |
| selfHealing | No | |
| nativeBrowser | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and idempotentHint=true, so the safety profile is covered. The description adds value by explaining what status fields the agent should inspect and how to interpret them (thresholds/actions), which is useful behavioral context beyond the annotations.
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?
The description is two sentences and every part earns its place: the field list gives what the tool reports, and the second sentence gives the trigger condition and expected action. It is front-loaded with the core status purpose before the usage guidance.
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?
Given this is a zero-parameter health-check tool, the annotations cover read-only/idempotent behavior, and an output schema exists, the description is complete. It even goes beyond the minimum by telling the agent when to call it and what to do with the returned values.
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?
The tool has zero parameters, so there is no input semantics for the description to clarify. The description instead compensates by naming the output fields that matter (cascade.totalCaptchas, rateLimiter.queueSize), which adds context beyond the empty schema.
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?
The description clearly identifies a health-check/status resource and enumerates the fields it exposes (cascade mode, transitions, rate-limiter usage, cache size, config). It lacks an explicit action verb like 'get' or 'return', but 'MCP server status' plus the title makes the purpose unambiguous and distinguishable from the search-oriented siblings.
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?
The description explicitly tells the agent when to call the tool: 'if searches start failing or returning empty'. It also gives follow-up guidance, telling the agent to check cascade.totalCaptchas and rateLimiter.queueSize and reduce search volume if they are high. This is a clear, actionable usage rule, and it implicitly separates this diagnostic tool from the search tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
project_memoryProject MemoryA
PROJECT MEMORY MANAGEMENT. With research enabled, one local broker lets multiple MCP sessions query the same knowledge base concurrently and orders writes safely. It links session intent, immutable plan revisions, experiments, decisions, versioned ontology, and bitemporal data lineage; corrections can use entity merge or split without discarding history. Use project_update for revision-checked changes to an existing project profile; context reads that same versioned profile. Use query_status/query_cancel with a caller-supplied search request_id to inspect or cooperatively stop a long retrieval. Use context for the bounded current project view; current to generate a compact CURRENT.md; get/get_batch for exact records and body spans; record/record_batch for durable idempotent writes; timeline/diff for history; sync for declared source deltas; job_status/job_wait/job_cancel for background progress and safe stage-boundary cancellation; verify for preservation levels; snapshot/snapshot_import/snapshot_rollback for versioned offline transfer and append-only recovery; doctor for passive diagnostics; rebuild for derived indexes; export for graph views; and forget for reversible deletion. Typed record writes return compact receipts and never echo submitted bodies. action=search remains a compatibility alias, but project_memory_search should be used for local knowledge retrieval. Ontology revisions use supersedes_term_id. Corrections preserve bitemporal data lineage and support assertion replacement plus entity merge or split. rebuild indexes approved local roots and code structure into a reproducible snapshot; export writes an interactive HTML viewer, Graphviz DOT, D3 JSON, or a Neo4j import bundle. HTML always includes PKM, Lineage, and Ontology tabs, an embedded-project selector, empty-canvas focus reset, and visible PNG or JSON download. forget previews impact before reversible project or assertion deletion. Search, search_parallel, scholar_search, and extract automatically capture sources and provenance; project_memory manages their durable structure.
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | Complete UTF-8 record body. Typed writes preserve it losslessly and return byte counts plus an exact get route. | |
| name | No | Project name for create/project_update, or entity name lookup for show. | |
| after | No | ||
| as_of | No | For context, reconstruct the project and plan view at this effective or recorded time. | |
| limit | No | Maximum local RAG results for action=search. | |
| query | No | Required for action=search. Searches stored local knowledge only and does not start live web discovery. | |
| roots | No | Approved local roots and optional path-independent role/alias manifest. sync merges roots by default; removal is explicit. | |
| title | No | Plan title, experiment name, or decision title. | |
| action | Yes | Typed project-memory operation. create: project_id and name required. project_update: versioned profile update with expected_revision and idempotency_key. get/get_batch: exact IDs and bounded body spans; body_bytes=0 returns metadata only. record/record_batch: typed durable writes with idempotency_key. context/current/timeline/diff: compact current or historical views. query_status/query_cancel: inspect or cooperatively cancel caller-identified local retrieval. sync/rebuild: incremental source and derived indexing. job_status/job_wait/job_cancel: durable background work. snapshot/snapshot_import/snapshot_rollback: offline transfer and append-only recovery. export: HTML, DOT, JSON, or Neo4j. forget: reversible deletion preview and confirmation. Unrelated local questions use project_memory_search. | |
| before | No | ||
| fields | No | Typed structured fields stored with the record revision. measurement requires metric, value, unit, evaluator_contract, dataset, split, tokenizer, context_length, targets, carrier_precision, and source_locator; byte_ledger requires byte_components, denominator, and scope. Put unknown required values in missing_fields. | |
| intent | No | Durable intent for record_type=session. | |
| job_id | No | ||
| reason | No | Required reason for any correction. | |
| status | No | Use success, failed, or inconclusive to finish an experiment. Omit or use running when starting it. | |
| aliases | No | Ontology term aliases, or aliases moved during entity_split. | |
| dry_run | No | ||
| filters | No | Optional project, source, type, role, lane, and recorded-time filters. Excluded lanes are not executed. | |
| metrics | No | Terminal experiment metrics. | |
| purpose | No | ||
| queries | No | Independent questions returned as separate ranked groups inside one broker request. | |
| records | No | ||
| summary | No | Experiment result or decision summary. | |
| version | No | Ontology revision number. Defaults to 1 or the superseded term version plus one. | |
| asset_id | No | Stable logical identity. Revisions remain under this ID. | |
| git_root | No | Optional Git root recorded with a rebuild snapshot. | |
| revision | No | ||
| valid_to | No | Corrected assertion valid-time end. | |
| artifacts | No | Legacy experiment artifact paths. Typed writes should use artifact_references; no values are silently sliced. | |
| body_path | No | Local UTF-8 file for a streamed versioned body attachment up to 256 MiB. Use instead of body; only a compact receipt is returned. | |
| operation | No | Typed durable write operation. Use with asset_kind and idempotency_key. | |
| protected | No | ||
| record_id | No | Known exact record or alias ID for get, diff, or verify. | |
| root_mode | No | ||
| target_id | No | Assertion or entity id for show or correction. | |
| asset_kind | No | ||
| body_bytes | No | UTF-8 bytes to return. Use 0 for metadata-only readback; use a positive value and next_body_offset for exact paging. | |
| hypothesis | No | Hypothesis when starting an experiment. | |
| project_id | No | Stable project id. For search, this is the write-isolated primary scope. Omit to list projects or use all_projects. | |
| record_ids | No | ||
| request_id | No | Caller-chosen ID for one local query. Supply it before a long request so project_memory query_status/query_cancel can inspect or cancel the same operation. | |
| source_ids | No | Entity ids merged into target_id by entity_merge. | |
| valid_from | No | Corrected assertion valid-time start. | |
| access_tier | No | Operational access tier. It does not rank scientific quality. | |
| body_offset | No | ||
| export_view | No | Initial HTML tab or non-HTML export scope. graph is PKM; ontology shows types, shared schema, verified identity links, and typed instances; lineage shows aligned data and research lineage. | graph |
| forget_mode | No | Preview first. Apply requires its confirm_token. Restore reverses deletion. | |
| record_type | No | Required for record. session stores intent; plan creates an immutable revision; experiment starts or finishes a run; decision links a conclusion; ontology creates a versioned type or relation; correction replaces an assertion or merges/splits entities. | |
| replacement | No | New assertion value, or new entity name for entity_split. | |
| set_current | No | For plan writes, select this revision as current. Defaults to true; use false for historical imports. | |
| to_revision | No | ||
| all_projects | No | Search or export every active named project. Excludes Inbox and cannot be combined with project ids. | |
| budget_bytes | No | ||
| detail_level | No | For show with project_id and no target_id, both values return a bounded summary. full is retained only for compatibility and never returns every durable record body. | summary |
| effective_at | No | ||
| evidence_ids | No | Evidence retained on a corrected assertion. | |
| cancel_reason | No | ||
| change_reason | No | Reason for a new plan revision. | |
| changed_paths | No | Explicit root-relative files or collection boundaries to re-read. Existing roots and all unlisted entries are preserved. | |
| confirm_token | No | Token returned by the matching forget preview. | |
| experiment_id | No | Experiment to finish or associate with a decision. | |
| export_format | No | Visualization file format. html writes one offline explorer with PKM, Lineage, Ontology, project selection, and current-view PNG or anonymized JSON download; d3 writes node-link JSON; dot writes Graphviz DOT; neo4j writes an import-ready CSV and Cypher bundle. | d3 |
| from_revision | No | ||
| memory_handle | No | Existing project session handle for a session intent revision. | |
| ontology_kind | No | Ontology term kind for record_type=ontology. | |
| removed_paths | No | Explicit root-relative entries to remove from the active snapshot. Historical snapshots remain addressable. | |
| snapshot_path | No | ||
| after_revision | No | ||
| artifact_limit | No | ||
| missing_fields | No | ||
| query_variants | No | Optional local retrieval variants for action=search. They run inside one broker request and are fused before one rerank. | |
| since_revision | No | ||
| artifact_offset | No | ||
| correction_kind | No | Correction operation. Defaults to assertion. | |
| idempotency_key | No | Stable operation key. Reuse it after an uncertain response. | |
| source_sequence | No | ||
| wait_timeout_ms | No | ||
| plan_revision_id | No | Plan revision associated with an experiment or decision. | |
| source_reference | No | ||
| user_constraints | No | ||
| correction_reason | No | ||
| expected_revision | No | Optimistic concurrency revision. Stale values return a conflict. | |
| supersedes_term_id | No | Prior ontology term replaced by this revision. | |
| verification_level | No | ||
| artifact_references | No | ||
| include_project_ids | No | Additional read-only projects for one local search or integrated visualization export. | |
| protected_parent_ids | No | ||
| response_deadline_ms | No | Caller-selected 1-300 second response deadline. Returns available lane results or QUERY_TIMEOUT with request_id. Native reads remain tracked until they settle; stored evidence and writes are unaffected. | |
| response_budget_bytes | No | Explicit result-array byte budget. Without an override, summaries adapt to unique result, question and source-family counts. Stored bodies and retrieval scope are not truncated by this budget; envelope metadata is separate. | |
| based_on_experiment_id | No | Experiment that motivated a plan revision. |
Output Schema
| Name | Required | Description |
|---|---|---|
| job | No | |
| diff | No | |
| meta | No | |
| error | No | |
| index | No | |
| plans | No | |
| query | No | |
| doctor | No | |
| entity | No | |
| forget | No | |
| groups | No | |
| memory | No | |
| record | No | |
| context | No | |
| current | No | |
| project | No | |
| receipt | No | |
| records | No | |
| results | No | |
| session | No | |
| entities | No | |
| imported | No | |
| projects | No | |
| receipts | No | |
| snapshot | No | |
| timeline | No | |
| assertion | No | |
| decisions | No | |
| elapsed_ms | No | |
| job_counts | No | |
| plan_count | No | |
| experiments | No | |
| entity_count | No | |
| query_status | No | |
| verification | No | |
| memory_handle | No | |
| session_count | No | |
| visualization | No | |
| decision_count | No | |
| document_count | No | |
| assertion_count | No | |
| correction_count | No | |
| experiment_count | No | |
| search_event_count | No | |
| source_entry_count | No | |
| active_source_snapshot | No | |
| entity_operation_count | No | |
| citation_observation_count | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations are all false (readOnlyHint=false, openWorldHint=false, idempotentHint=false), so they declare nothing positive and the description carries the burden. It adds meaningful behavioral context: concurrent querying with safe write ordering, 'Typed record writes return compact receipts and never echo submitted bodies,' corrections that 'preserve bitemporal data lineage and support assertion replacement plus entity merge or split,' forget that 'previews impact before reversible project or assertion deletion,' and that search-family tools 'automatically capture sources and provenance.' No contradiction with annotations. Small deduction for not covering auth requirements, rate limits, or failure semantics beyond hints like QUERY_TIMEOUT.
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?
The description is dense and every sentence carries information, but it is a very long wall of text with essentially no paragraph breaks for scannability. The central routing sentence is a massive run-on listing 15+ action mappings in one clause ('Use context for the bounded current project view; current to generate a compact CURRENT.md; get/get_batch for exact records and body spans; record/record_batch for durable idempotent writes; ...'). The core concept is front-loaded well, but an agent parsing this would benefit from bullets or segmentation. Appropriately sized for the tool's complexity, poorly structured for consumption.
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 90-parameter destination tool with an output schema, this is near-complete: it covers the broker/concurrency model, what the store links, a full action routing table, behavior of writes, correction semantics, export output characteristics, and sibling differentiation. The output schema means return values need no explanation. Deductions for not addressing access control/permissions, rate limits, or multi-step error-recovery flows beyond idempotency-key mentions. Given the enormous scope, the coverage is strong but not exhaustive.
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 66%, so the schema already documents most parameters and the baseline is 3. The description adds value mostly at the action-routing level (which parameter combinations belong to which action) and via behavioral notes like 'Ontology revisions use supersedes_term_id' and export format specifics (HTML always includes PKM, Lineage, and Ontology tabs). However, it does not deeply compensate for the remaining undocumented ~34% of parameters; many parameter meanings are only discoverable through the schema or the action enum's own long description. Marginal addition over the schema, hence a 3.
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?
The description states a specific role: a local broker that lets multiple MCP sessions query the same knowledge base concurrently while ordering writes safely, and it enumerates what the store links (session intent, plan revisions, experiments, decisions, versioned ontology, bitemporal lineage). It also distinguishes itself from siblings by explicitly naming project_memory_search as the sibling for local knowledge retrieval. Slight deduction because the tool is a 25-plus-action dispatcher, so the 'purpose' is necessarily broad rather than a single verb+resource.
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?
The description provides an explicit routing map for nearly every action: 'Use project_update for revision-checked changes...', 'Use context for the bounded current project view; current to generate a compact CURRENT.md; get/get_batch for exact records...', plus job_*, snapshot_*, doctor, rebuild, export, and forget. It also gives a negative rule: 'action=search remains a compatibility alias, but project_memory_search should be used' and 'Unrelated local questions use project_memory_search.' This is model-guidance of the highest quality.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
project_memory_searchLocal Project Memory SearchARead-onlyIdempotent
LOCAL PROJECT KNOWLEDGE SEARCH ONLY. With research enabled, one local broker lets multiple MCP sessions query the same knowledge base concurrently and orders writes safely. Always use this tool when the user asks to find, recall, inspect, or search information already stored in project memory, indexed local roots, papers, codebases, plans, experiments, or decisions. Use query_variants for multiple retrieval angles in one call instead of opening terminals or calling this tool repeatedly. Query embeddings are batched, candidates are fused with RRF, graph expansion starts from retrieved evidence, and the primary query is reranked once. Only bounded query-focused summaries from that final ranking are returned; stored bodies never bypass this response gate. If one retrieval lane times out, the other lanes still return and meta.degraded_lanes identifies the partial lane; do not repeat the same query automatically. Uses exact, BM25, vector, and graph retrieval. It never opens Google, a browser, or SearchApi. Use search or search_parallel only when new external information is required.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum local RAG results. | |
| query | No | One natural-language query over indexed local project knowledge. Use queries instead for independent grouped answers. | |
| filters | No | Optional project, source, type, role, lane, and recorded-time filters. Excluded lanes are not executed. | |
| queries | No | Independent questions returned as separate ranked groups inside one broker request. | |
| project_id | No | Primary project to search. Required unless all_projects=true. | |
| request_id | No | Caller-chosen ID for one local query. Supply it before a long request so project_memory query_status/query_cancel can inspect or cancel the same operation. | |
| all_projects | No | Search every active named project. Excludes Inbox and cannot be combined with project ids. | |
| query_variants | No | Optional retrieval variants executed inside this one broker request. Exact identifiers and quoted phrases are added deterministically, candidates are fused with RRF, and the primary query is reranked once. Do not make repeated terminal or tool calls for query variants. | |
| include_project_ids | No | Additional read-only projects searched with the primary project. | |
| response_deadline_ms | No | Caller-selected 1-300 second response deadline. Returns available lane results or QUERY_TIMEOUT with request_id. Native reads remain tracked until they settle; stored evidence and writes are unaffected. | |
| response_budget_bytes | No | Explicit result-array byte budget. Without an override, summaries adapt to unique result, question and source-family counts. Stored bodies and retrieval scope are not truncated by this budget; envelope metadata is separate. |
Output Schema
| Name | Required | Description |
|---|---|---|
| job | No | |
| diff | No | |
| meta | No | |
| error | No | |
| index | No | |
| plans | No | |
| query | No | |
| doctor | No | |
| entity | No | |
| forget | No | |
| groups | No | |
| memory | No | |
| record | No | |
| context | No | |
| current | No | |
| project | No | |
| receipt | No | |
| records | No | |
| results | No | |
| session | No | |
| entities | No | |
| imported | No | |
| projects | No | |
| receipts | No | |
| snapshot | No | |
| timeline | No | |
| assertion | No | |
| decisions | No | |
| elapsed_ms | No | |
| job_counts | No | |
| plan_count | No | |
| experiments | No | |
| entity_count | No | |
| query_status | No | |
| verification | No | |
| memory_handle | No | |
| session_count | No | |
| visualization | No | |
| decision_count | No | |
| document_count | No | |
| assertion_count | No | |
| correction_count | No | |
| experiment_count | No | |
| search_event_count | No | |
| source_entry_count | No | |
| active_source_snapshot | No | |
| entity_operation_count | No | |
| citation_observation_count | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already show readOnlyHint=true, openWorldHint=false, and idempotentHint=true, and the description adds substantial non-obvious behavior: broker-based concurrency and write ordering, RRF fusion and reranking, the response gate that returns only bounded summaries, degraded-lane timeout behavior, and the explicit guarantee that it never opens external sources. No contradiction exists.
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?
The opening line is emphatic and front-loaded, and the paragraph is dense rather than padded. Some redundancy exists (retrieval lanes are named both as behavior and as a parameter concept), and the broker/concurrency sentence is useful context but not essential to invoking the tool. Overall, nearly every sentence earns its place, though a slightly tighter structure would improve scannability.
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 an 11-parameter tool with nested objects and an output schema, the description covers the critical contexts: when to choose it, what it will not do, how failures surface, how results are bounded, and how variants/timeouts behave. The schema already documents parameter details, so the description does not need to repeat them; nothing essential is missing.
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 coverage is 100%, so the baseline is 3, but the description adds operational meaning beyond parameter names: query_variants is framed as the preferred way to get multiple retrieval angles, its batching/RRF/rerank behavior is described, and response_deadline_ms semantics are tied to QUERY_TIMEOUT and request_id. This is meaningful value on top of the schema.
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?
The description names a specific verb and resource: searching local project knowledge, and immediately scopes it with 'LOCAL PROJECT KNOWLEDGE SEARCH ONLY.' It explicitly lists user intents that should route here (find, recall, inspect, search) and contrasts itself with external search tools, so an agent can distinguish it from siblings without ambiguity.
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?
It gives an explicit when-to-use rule ('Always use this tool when the user asks to find, recall, inspect, or search information already stored in project memory...') and an explicit exclusion ('Use search or search_parallel only when new external information is required'). It also instructs the agent to prefer query_variants over repeated calls and tells it not to auto-retry after degraded lanes.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
scholar_searchGoogle Scholar SearchA
Use only for paper metadata such as authors, venue, year, versions, and citation counts. With research enabled, one local broker lets multiple MCP sessions query the same knowledge base concurrently and orders writes safely. Do not use it to discover or read paper content; use search with extract_mode instead. Returns title, authors, publication, year, snippet, citation count, related/version links, and an available full-text link. With research enabled, metadata and citation observations retain provider provenance and research_context exposes related prior searches. Google Scholar uses browser search, SearchApi primary, or configured fallback. Results are cached with the same TTL as search. Max 10 papers per call.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum papers. Integer 1-10, default 10. | |
| query | Yes | Google Scholar query. Supports quotes and author: operators. | |
| project_id | No | Project memory id. | |
| session_id | No | Stable host task id. Reuses the same project session after restart. | |
| memory_handle | No | Reuse the handle returned by a prior project-aware call. | |
| session_intent | No | Current durable task intent. A changed value creates an immutable revision. |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | |
| error | No | |
| query | No | |
| memory | No | |
| results | No | |
| elapsed_ms | No | |
| memory_handle | No | |
| research_context | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds meaningful behavioral detail beyond the annotations, such as the underlying provider chain ('browser search, SearchApi primary, or configured fallback'), caching behavior ('cached with the same TTL as search'), and output contents like full-text links. It also hints at write behavior ('orders writes safely'), which is consistent with readOnlyHint=false. The research-enabled concurrency and provenance details are less directly actionable but still informative.
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?
The description is front-loaded with the most important usage boundary, but it includes dense and somewhat tangential clauses about 'research enabled' behavior, provenance, and broker concurrency that blur the core message. It is not overly long, yet it could be tightened by separating the essential selection guidance from optional background details.
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?
The description covers the core use case, exclusions, alternative tools, output fields, provider behavior, caching, and a hard result limit. Given the rich input and output schemas already present, only minor gaps remain—such as the precise effect of the research-mode parameters on writes—but nothing essential for typical tool selection and invocation is missing.
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 100%, so the schema already documents all six parameters thoroughly. The description repeats the max limit ('Max 10 papers per call') but does not add much meaning for parameters like project_id, session_id, memory_handle, or session_intent beyond what the schema already states. This lands at the baseline of 3.
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?
The description clearly states that this tool is for paper metadata retrieval only, listing specific fields like authors, venue, year, versions, and citation counts. It also explicitly contrasts itself with 'search with extract_mode' for reading full paper content, making its scope unmistakable relative to sibling tools.
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?
The description gives direct usage boundaries: 'Use only for paper metadata' and 'Do not use it to discover or read paper content; use search with extract_mode instead.' It names the alternative tool and the condition for choosing it, leaving no ambiguity about when scholar_search is appropriate.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchWeb Search and ExtractA
PRIMARY SINGLE-QUERY LIVE DISCOVERY AND CONTENT INGESTION TOOL. ALWAYS PERFORMS LIVE WEB SEARCH. With research enabled, one local broker lets multiple MCP sessions query the same knowledge base concurrently and orders writes safely. Use this tool only when new external information from Google, public websites, papers, or repositories is required. When the task requires both finding and reading public web pages, PDFs, papers, or GitHub repositories, set extract_mode=abstract or full in this call. Do not download public PDFs, clone repositories, or invoke local parsers first. Select extract_mode=full, not abstract, when the user asks to read originals, full text, document bodies, or code, or to compare source contents. Use extract separately only when the exact public URL is already known and no new discovery is required. Use general repository tools only for editing, building, testing, or full Git history. Providing project_id also fuses stored project evidence with live results, but never makes this a local-only search. For stored project knowledge without live web discovery, use project_memory_search. limit accepts integers from 1 to 20. extract_limit accepts integers from 1 to 10, defaults to 5, and limits unique extracted URLs. Final results use one bounded response budget after ranking; full captured bodies remain local. Use extract on one selected URL when longer response text is explicitly required. The response includes applied, skipped, truncated, total_chars, and a bounded remaining_urls list. GitHub none mode reads the README; abstract and full can sparse-index eligible repositories with Tree-sitter. With research enabled and project_id set, live web, exact, BM25, vector, code, and graph lanes are fused by RRF and one reranker. With research disabled, provider order and query BM25 rank are fused by a lightweight in-memory RRF reranker without opening local storage or loading the vector model. research_context returns up to three prior searches for deeper or adjacent follow-up work. Captured search, source, session, and project provenance form data lineage; extracted bodies become evidence while unread hits remain metadata. include_project_ids adds read-only cross-project retrieval through versioned ontology and verified schema/entity links without merging records. Browser search is the no-key default and SearchApi is the configured primary or fallback provider. Results are cached for 24h unless the TTL is disabled.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum results. Integer 1-20, default 10. | |
| query | Yes | Google search query. Use site: filters and quotes for exact match. | |
| max_chars | No | Maximum returned characters per extracted result in response_content=full. Defaults to 1500 for abstract and 50000 for full. | |
| project_id | No | Project memory id. | |
| session_id | No | Stable host task id. Reuses the same project session after restart. | |
| extract_mode | No | Content depth in this search call. Use none only when titles and snippets are enough, abstract for relevance evidence, and full when the user asks to read originals, full text, document bodies, or code, or to compare source contents. Use abstract or full here instead of following search with separate extraction, PDF download, or repository clone. For GitHub results, none reads the README; abstract and full use the same repository eligibility gate but index different source amounts. | none |
| extract_limit | No | Maximum unique result URLs to extract. Integer 1-10, default 5. | |
| memory_handle | No | Reuse the handle returned by a prior project-aware call. | |
| session_intent | No | Current durable task intent. A changed value creates an immutable revision. | |
| response_content | No | Controls only the returned body. summary is the default and returns a 1500-character evidence excerpt; full returns up to max_chars. Research storage keeps the full captured text in deterministic chunks. | summary |
| include_project_ids | No | Additional read-only projects joined through ontology-aligned schema and identity links. New records stay in project_id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | |
| error | No | |
| query | No | |
| memory | No | |
| results | No | |
| elapsed_ms | No | |
| memory_handle | No | |
| research_context | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations provide readOnlyHint=false, openWorldHint=true, and idempotentHint=false, but the description adds substantial behavioral context: it performs live web searches, caches results for 24h, describes response fields (applied, skipped, truncated, total_chars, bounded remaining_urls), explains research-mode fusion, and notes provenance tracking. None of this contradicts the annotations; it enriches them significantly.
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?
The description is a dense single paragraph of over 300 words. While every sentence adds value, the lack of structure (no bullets, headings, or logical breaks) makes it hard to scan. It front-loads the primary purpose well, but the sheer length and monolithic formatting hurt readability. It is informative but not concise or well-organized, so a 3 is appropriate.
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?
Given the tool's complexity (11 parameters, enums, output schema, multiple modes), the description is remarkably complete. It covers use cases, extraction modes, research-mode behavior, caching, response structure, provenance, and sibling differentiation. The output schema exists, so return values are documented there. Nothing critical for an agent to correctly invoke the tool is missing.
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?
The input schema already documents all 11 parameters (coverage 100%), so the baseline is 3. However, the description adds meaning beyond the schema: it clarifies when to set extract_mode (abstract vs full), explains the relationship between extract_limit and unique URLs, details the effect of response_content, and describes how include_project_ids works with read-only cross-project retrieval. This goes beyond what the schema's individual descriptions provide, justifying a 4.
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?
The description opens with 'PRIMARY SINGLE-QUERY LIVE DISCOVERY AND CONTENT INGESTION TOOL. ALWAYS PERFORMS LIVE WEB SEARCH.' which states a specific verb, resource, and scope. It explicitly contrasts itself with siblings like project_memory_search and extract, clarifying it is for external live information only. This removes any ambiguity about what the tool does.
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?
It gives explicit when-to-use criteria: 'Use this tool only when new external information from Google, public websites, papers, or repositories is required.' It also names alternatives and conditions, such as 'Use extract separately only when the exact public URL is already known' and 'For stored project knowledge without live web discovery, use project_memory_search.' This is textbook usage guidance with clear exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_parallelParallel Web Search and ExtractA
PRIMARY MULTI-QUERY LIVE DISCOVERY AND CONTENT INGESTION TOOL. ALWAYS PERFORMS MULTIPLE LIVE WEB SEARCHES. With research enabled, one local broker lets multiple MCP sessions query the same knowledge base concurrently and orders writes safely. Use this tool only when 2-12 new external queries are required. Providing project_id adds stored evidence but never makes the searches local-only. When the task requires broad discovery plus reading public web pages, PDFs, papers, or GitHub repositories, set extract_mode=abstract or full in this call. Do not download public PDFs, clone repositories, or invoke local parsers first. Select extract_mode=full, not abstract, when the user asks to read originals, full text, document bodies, or code, or to compare source contents. Use extract separately only when the exact public URL is already known and no new discovery is required. Use general repository tools only for editing, building, testing, or full Git history. For stored project knowledge without live web discovery, use project_memory_search. Each query limit accepts integers from 1 to 20. Call-wide extract_limit accepts 1-20 with default 12 for abstract, and 1-10 with default 10 for full. Final results use one call-wide bounded response budget after ranking; at most 36 ranked rows are returned across query groups and full captured bodies remain local. Use extract on one selected URL when longer response text is explicitly required. The response includes applied, skipped, truncated, total_chars, and a bounded remaining_urls list. GitHub none mode reads the README; abstract and full can sparse-index eligible repositories with Tree-sitter. With research enabled and project_id set, each query fuses live, exact, BM25, vector, code, and graph lanes through RRF and one reranker. With research disabled, each query independently fuses provider order and query BM25 rank through a lightweight in-memory RRF reranker without opening local storage or loading the vector model. research_context returns prior project searches; capture retains data lineage and include_project_ids uses versioned ontology and verified cross-project schema/entity links. Extracted bodies become evidence while unread hits remain metadata. Native Chrome uses one authenticated local broker across MCP sessions and keeps one hidden process with up to four reusable tabs. Query starts are staggered and each tab continuously consumes the remaining queue. SearchApi fallback replaces failed queries individually.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum results per query. Integer 1-20, default 10. | |
| queries | Yes | 2-12 independent live queries. | |
| max_chars | No | Maximum returned characters per extracted result in response_content=full. Defaults to 1500 for abstract and 50000 for full. | |
| project_id | No | Project memory id. | |
| session_id | No | Stable host task id. Reuses the same project session after restart. | |
| extract_mode | No | Content depth in this search call. Use none only when titles and snippets are enough, abstract for relevance evidence, and full when the user asks to read originals, full text, document bodies, or code, or to compare source contents. Use abstract or full here instead of following search with separate extraction, PDF download, or repository clone. For GitHub results, none reads the README; abstract and full use the same repository eligibility gate but index different source amounts. | none |
| extract_limit | No | Call-wide maximum unique result URLs to extract. Integer 1-20, default 12 for abstract; full defaults to and allows at most 10. | |
| memory_handle | No | Reuse the handle returned by a prior project-aware call. | |
| session_intent | No | Current durable task intent. A changed value creates an immutable revision. | |
| response_content | No | Controls only returned bodies. summary is the default and returns 1500-character evidence excerpts; full returns up to max_chars. Research storage keeps the full captured text in deterministic chunks. | summary |
| include_project_ids | No | Additional read-only projects joined through ontology-aligned schema and identity links. New records stay in project_id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| meta | No | |
| error | No | |
| memory | No | |
| results | No | |
| elapsed_ms | No | |
| memory_handle | No | |
| research_context | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations are minimal (readOnlyHint false, openWorldHint true, idempotentHint false), so the description carries the full burden. It discloses writes ('orders writes safely'), concurrency ('one local broker lets multiple MCP sessions query the same knowledge base concurrently'), fallback behavior ('SearchApi fallback replaces failed queries individually'), and result bounds ('at most 36 ranked rows are returned'). This far exceeds what annotations provide and is fully consistent with them.
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?
The description is a single dense paragraph exceeding 300 words. While every sentence adds substantive information, the lack of paragraph breaks or bullets makes it harder to scan. The content is front-loaded with the primary purpose, but the wall of text could be better structured without losing information. It is not concise, though it is thorough.
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?
With 11 parameters, an output schema, and complex behavioral nuances (research modes, extract modes, browser fallback, response budgets), the description covers all essential aspects. It explains return fields ('applied, skipped, truncated, total_chars, and a bounded remaining_urls list'), clarifies modes, and addresses edge cases like project_id not making searches local-only. An agent can confidently invoke this tool correctly.
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 coverage is 100% and parameter descriptions are already detailed. The description adds meaningful context beyond the schema, such as clarifying the relationship between extract_mode and extract_limit, explaining that 'Use abstract or full here instead of following search with separate extraction, PDF download, or repository clone,' and detailing the response budget. This adds value beyond the schema, though not every nuance is covered.
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?
The description opens with a clear statement of what the tool does: 'PRIMARY MULTI-QUERY LIVE DISCOVERY AND CONTENT INGESTION TOOL. ALWAYS PERFORMS MULTIPLE LIVE WEB SEARCHES.' It also distinguishes itself from siblings by specifying when to use it: 'Use this tool only when 2-12 new external queries are required.' This makes its purpose unambiguous and clearly differentiated from extract, project_memory_search, and repository tools.
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?
The description provides explicit usage guidance: 'Use this tool only when 2-12 new external queries are required,' 'Use extract separately only when the exact public URL is already known and no new discovery is required,' and 'Use general repository tools only for editing, building, testing, or full Git history.' It also names alternatives like project_memory_search for stored knowledge without live discovery. This is comprehensive and actionable.
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.
7 tool updates
v1.1.3- Changed
extract1 field changed- changed
Output schema / properties / error / anyOfPrevious value: -[ - { - "type": "string" - }, - { - "additionalProperties": false, - "properties": { - "code": { - "type": "string" - }, - "message": { - "type": "string" - }, - "retry_after_ms": { - "type": "number" - }, - "retryable": { - "type": "boolean" - }, - "user_action": { - "type": "string" - } - }, - "required": [ - "code", - "message", - "retryable" - ], - "type": "object" - } -]New value: +[ + { + "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "cancel_reason": { + "type": "string" + }, + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "phase": { + "type": "string" + }, + "request_id": { + "type": "string" + }, + "retry_after_ms": { + "type": "number" + }, + "retryable": { + "type": "boolean" + }, + "user_action": { + "type": "string" + } + }, + "required": [ + "code", + "message", + "retryable" + ], + "type": "object" + } +]
- Changed
health3 fields changed- added
Output schema / properties / error / properties / cancel_reasonAdded value: +{ + "type": "string" +} - added
Output schema / properties / error / properties / phaseAdded value: +{ + "type": "string" +} - added
Output schema / properties / error / properties / request_idAdded value: +{ + "type": "string" +}
- Changed
project_memory84 fields changed- added
Input schema / properties / access_tierAdded value: +{ + "description": "Operational access tier. It does not rank scientific quality.", + "enum": [ + "hot", + "warm", + "cold" + ], + "type": "string" +} - changed
Input schema / properties / action / descriptionPrevious value: -"create: project_id and name required. show: inspect a known project, assertion, entity, or durable record. search: compatibility alias for project_memory_search. record: project_id and record_type required. rebuild: project_id required; roots optional. export: project_id, include_project_ids, or all_projects=true required. forget: project_id and forget_mode required; apply also requires confirm_token."New value: +"Typed project-memory operation. create: project_id and name required. project_update: versioned profile update with expected_revision and idempotency_key. get/get_batch: exact IDs and bounded body spans; body_bytes=0 returns metadata only. record/record_batch: typed durable writes with idempotency_key. context/current/timeline/diff: compact current or historical views. query_status/query_cancel: inspect or cooperatively cancel caller-identified local retrieval. sync/rebuild: incremental source and derived indexing. job_status/job_wait/job_cancel: durable background work. snapshot/snapshot_import/snapshot_rollback: offline transfer and append-only recovery. export: HTML, DOT, JSON, or Neo4j. forget: reversible deletion preview and confirmation. Unrelated local questions use project_memory_search." - changed
Input schema / properties / action / enumPrevious value: -[ - "create", - "show", - "search", - "record", - "rebuild", - "export", - "forget" -]New value: +[ + "create", + "project_update", + "show", + "search", + "record", + "record_batch", + "get", + "get_batch", + "context", + "timeline", + "diff", + "sync", + "job_status", + "job_wait", + "job_cancel", + "query_status", + "query_cancel", + "verify", + "current", + "snapshot", + "snapshot_import", + "snapshot_rollback", + "doctor", + "rebuild", + "export", + "forget" +] - added
Input schema / properties / afterAdded value: +{ + "type": "string" +} - added
Input schema / properties / after_revisionAdded value: +{ + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / artifact_limitAdded value: +{ + "maximum": 1000, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / artifact_offsetAdded value: +{ + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / artifact_referencesAdded value: +{ + "items": { + "properties": { + "artifact_id": { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "available": { + "type": "boolean" + }, + "content_hash": { + "maxLength": 200, + "type": "string" + }, + "location": { + "maxLength": 2000, + "type": "string" + }, + "media_type": { + "maxLength": 200, + "type": "string" + }, + "member": { + "maxLength": 2000, + "type": "string" + }, + "observed_at": { + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "path": { + "maxLength": 2000, + "type": "string" + }, + "role": { + "maxLength": 200, + "type": "string" + }, + "size": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "verification": { + "enum": [ + "declared", + "present", + "hash_verified", + "restore_tested", + "replayed" + ], + "type": "string" + } + }, + "required": [ + "artifact_id" + ], + "type": "object" + }, + "maxItems": 50000, + "type": "array" +} - changed
Input schema / properties / artifacts / descriptionPrevious value: -"Experiment artifact paths or identifiers."New value: +"Legacy experiment artifact paths. Typed writes should use artifact_references; no values are silently sliced." - changed
Input schema / properties / artifacts / maxItemsPrevious value: -100New value: +50000 - added
Input schema / properties / as_ofAdded value: +{ + "description": "For context, reconstruct the project and plan view at this effective or recorded time.", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" +} - added
Input schema / properties / asset_idAdded value: +{ + "description": "Stable logical identity. Revisions remain under this ID.", + "maxLength": 500, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / asset_kindAdded value: +{ + "enum": [ + "project", + "plan", + "task", + "method", + "experiment", + "attempt", + "arm", + "measurement", + "byte_ledger", + "artifact", + "document", + "decision", + "session" + ], + "type": "string" +} - added
Input schema / properties / beforeAdded value: +{ + "type": "string" +} - changed
Input schema / properties / body / descriptionPrevious value: -"Plan body for record_type=plan."New value: +"Complete UTF-8 record body. Typed writes preserve it losslessly and return byte counts plus an exact get route." - changed
Input schema / properties / body / maxLengthPrevious value: -50000New value: +4194304 - added
Input schema / properties / body_bytesAdded value: +{ + "description": "UTF-8 bytes to return. Use 0 for metadata-only readback; use a positive value and next_body_offset for exact paging.", + "maximum": 1048576, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / body_offsetAdded value: +{ + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / body_pathAdded value: +{ + "description": "Local UTF-8 file for a streamed versioned body attachment up to 256 MiB. Use instead of body; only a compact receipt is returned.", + "maxLength": 4000, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / budget_bytesAdded value: +{ + "maximum": 1000000, + "minimum": 1000, + "type": "integer" +} - added
Input schema / properties / cancel_reasonAdded value: +{ + "maxLength": 500, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / changed_pathsAdded value: +{ + "description": "Explicit root-relative files or collection boundaries to re-read. Existing roots and all unlisted entries are preserved.", + "items": { + "properties": { + "path": { + "maxLength": 2000, + "minLength": 1, + "type": "string" + }, + "root": { + "maxLength": 32, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "root", + "path" + ], + "type": "object" + }, + "maxItems": 100000, + "type": "array" +} - added
Input schema / properties / correction_reasonAdded value: +{ + "maxLength": 2000, + "type": "string" +} - added
Input schema / properties / dry_runAdded value: +{ + "type": "boolean" +} - added
Input schema / properties / effective_atAdded value: +{ + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" +} - added
Input schema / properties / expected_revisionAdded value: +{ + "description": "Optimistic concurrency revision. Stale values return a conflict.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / fieldsAdded value: +{ + "additionalProperties": {}, + "description": "Typed structured fields stored with the record revision. measurement requires metric, value, unit, evaluator_contract, dataset, split, tokenizer, context_length, targets, carrier_precision, and source_locator; byte_ledger requires byte_components, denominator, and scope. Put unknown required values in missing_fields.", + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Input schema / properties / filtersAdded value: +{ + "description": "Optional project, source, type, role, lane, and recorded-time filters. Excluded lanes are not executed.", + "properties": { + "kinds": { + "items": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "recorded_after": { + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "recorded_before": { + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "retrieval_lanes": { + "items": { + "enum": [ + "exact", + "bm25", + "vector", + "graph" + ], + "type": "string" + }, + "maxItems": 4, + "type": "array" + }, + "roles": { + "items": { + "enum": [ + "experiment_record", + "result", + "method", + "log", + "paper", + "plan", + "config", + "source", + "report", + "other" + ], + "type": "string" + }, + "maxItems": 10, + "type": "array" + }, + "source_families": { + "items": { + "enum": [ + "document", + "code", + "graph" + ], + "type": "string" + }, + "maxItems": 3, + "type": "array" + } + }, + "type": "object" +} - added
Input schema / properties / from_revisionAdded value: +{ + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / idempotency_keyAdded value: +{ + "description": "Stable operation key. Reuse it after an uncertain response.", + "maxLength": 500, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / job_idAdded value: +{ + "maxLength": 500, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / missing_fieldsAdded value: +{ + "items": { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "maxItems": 1000, + "type": "array" +} - changed
Input schema / properties / name / descriptionPrevious value: -"Project name for create, or entity name lookup for show."New value: +"Project name for create/project_update, or entity name lookup for show." - added
Input schema / properties / operationAdded value: +{ + "description": "Typed durable write operation. Use with asset_kind and idempotency_key.", + "enum": [ + "create", + "patch", + "append_evidence", + "relocate", + "correct" + ], + "type": "string" +} - added
Input schema / properties / protectedAdded value: +{ + "type": "boolean" +} - added
Input schema / properties / protected_parent_idsAdded value: +{ + "items": { + "maxLength": 500, + "type": "string" + }, + "maxItems": 10000, + "type": "array" +} - added
Input schema / properties / purposeAdded value: +{ + "maxLength": 10000, + "type": "string" +} - added
Input schema / properties / queriesAdded value: +{ + "description": "Independent questions returned as separate ranked groups inside one broker request.", + "items": { + "properties": { + "id": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "limit": { + "maximum": 20, + "minimum": 1, + "type": "integer" + }, + "query": { + "maxLength": 400, + "minLength": 1, + "type": "string" + }, + "query_variants": { + "items": { + "maxLength": 400, + "minLength": 1, + "type": "string" + }, + "maxItems": 19, + "type": "array" + } + }, + "required": [ + "id", + "query" + ], + "type": "object" + }, + "maxItems": 20, + "minItems": 1, + "type": "array" +} - added
Input schema / properties / record_idAdded value: +{ + "description": "Known exact record or alias ID for get, diff, or verify.", + "maxLength": 500, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / record_idsAdded value: +{ + "items": { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "maxItems": 100, + "minItems": 1, + "type": "array" +} - added
Input schema / properties / recordsAdded value: +{ + "items": { + "properties": { + "access_tier": { + "enum": [ + "hot", + "warm", + "cold" + ], + "type": "string" + }, + "aliases": { + "items": { + "maxLength": 2000, + "minLength": 1, + "type": "string" + }, + "maxItems": 1000, + "type": "array" + }, + "artifact_references": { + "items": { + "properties": { + "artifact_id": { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "available": { + "type": "boolean" + }, + "content_hash": { + "maxLength": 200, + "type": "string" + }, + "location": { + "maxLength": 2000, + "type": "string" + }, + "media_type": { + "maxLength": 200, + "type": "string" + }, + "member": { + "maxLength": 2000, + "type": "string" + }, + "observed_at": { + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "path": { + "maxLength": 2000, + "type": "string" + }, + "role": { + "maxLength": 200, + "type": "string" + }, + "size": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "verification": { + "enum": [ + "declared", + "present", + "hash_verified", + "restore_tested", + "replayed" + ], + "type": "string" + } + }, + "required": [ + "artifact_id" + ], + "type": "object" + }, + "maxItems": 50000, + "type": "array" + }, + "asset_id": { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "body": { + "maxLength": 4194304, + "type": "string" + }, + "body_path": { + "maxLength": 4000, + "minLength": 1, + "type": "string" + }, + "correction_reason": { + "maxLength": 2000, + "type": "string" + }, + "effective_at": { + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "evidence_ids": { + "items": { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "maxItems": 50000, + "type": "array" + }, + "expected_revision": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "fields": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "idempotency_key": { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "kind": { + "enum": [ + "project", + "plan", + "task", + "method", + "experiment", + "attempt", + "arm", + "measurement", + "byte_ledger", + "artifact", + "document", + "decision", + "session" + ], + "type": "string" + }, + "missing_fields": { + "items": { + "maxLength": 500, + "minLength": 1, + "type": "string" + }, + "maxItems": 1000, + "type": "array" + }, + "operation": { + "enum": [ + "create", + "patch", + "append_evidence", + "relocate", + "correct" + ], + "type": "string" + }, + "protected": { + "type": "boolean" + }, + "set_current": { + "type": "boolean" + }, + "source_reference": { + "maxLength": 2000, + "type": "string" + }, + "source_sequence": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "kind", + "operation", + "idempotency_key" + ], + "type": "object" + }, + "maxItems": 100, + "minItems": 1, + "type": "array" +} - added
Input schema / properties / removed_pathsAdded value: +{ + "description": "Explicit root-relative entries to remove from the active snapshot. Historical snapshots remain addressable.", + "items": { + "properties": { + "path": { + "maxLength": 2000, + "minLength": 1, + "type": "string" + }, + "root": { + "maxLength": 32, + "minLength": 1, + "type": "string" + } + }, + "required": [ + "root", + "path" + ], + "type": "object" + }, + "maxItems": 100000, + "type": "array" +} - added
Input schema / properties / request_idAdded value: +{ + "description": "Caller-chosen ID for one local query. Supply it before a long request so project_memory query_status/query_cancel can inspect or cancel the same operation.", + "maxLength": 200, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / response_budget_bytesAdded value: +{ + "description": "Explicit result-array byte budget. Without an override, summaries adapt to unique result, question and source-family counts. Stored bodies and retrieval scope are not truncated by this budget; envelope metadata is separate.", + "maximum": 1000000, + "minimum": 1000, + "type": "integer" +} - added
Input schema / properties / response_deadline_msAdded value: +{ + "description": "Caller-selected 1-300 second response deadline. Returns available lane results or QUERY_TIMEOUT with request_id. Native reads remain tracked until they settle; stored evidence and writes are unaffected.", + "maximum": 300000, + "minimum": 1000, + "type": "integer" +} - added
Input schema / properties / revisionAdded value: +{ + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / root_modeAdded value: +{ + "enum": [ + "merge", + "replace", + "remove" + ], + "type": "string" +} - changed
Input schema / properties / roots / descriptionPrevious value: -"Approved local roots to index. Omit to rebuild derived state from stored sources."New value: +"Approved local roots and optional path-independent role/alias manifest. sync merges roots by default; removal is explicit." - added
Input schema / properties / roots / items / properties / manifestAdded value: +{ + "items": { + "properties": { + "aliases": { + "items": { + "maxLength": 2000, + "minLength": 1, + "type": "string" + }, + "maxItems": 100, + "type": "array" + }, + "path": { + "maxLength": 2000, + "minLength": 1, + "type": "string" + }, + "role": { + "enum": [ + "experiment_record", + "result", + "method", + "log", + "paper", + "plan", + "config", + "source", + "report", + "other" + ], + "type": "string" + } + }, + "required": [ + "path", + "role" + ], + "type": "object" + }, + "maxItems": 100000, + "type": "array" +} - added
Input schema / properties / roots / items / properties / roleAdded value: +{ + "enum": [ + "experiment_record", + "result", + "method", + "log", + "paper", + "plan", + "config", + "source", + "report", + "other" + ], + "type": "string" +} - changed
Input schema / properties / roots / maxItemsPrevious value: -8New value: +64 - added
Input schema / properties / set_currentAdded value: +{ + "description": "For plan writes, select this revision as current. Defaults to true; use false for historical imports.", + "type": "boolean" +} - added
Input schema / properties / since_revisionAdded value: +{ + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / snapshot_pathAdded value: +{ + "maxLength": 4000, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / source_referenceAdded value: +{ + "maxLength": 2000, + "type": "string" +} - added
Input schema / properties / source_sequenceAdded value: +{ + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / to_revisionAdded value: +{ + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / user_constraintsAdded value: +{ + "items": { + "maxLength": 2000, + "type": "string" + }, + "maxItems": 1000, + "type": "array" +} - changed
Input schema / properties / valid_from / patternPrevious value: -"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"New value: +"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$" - changed
Input schema / properties / valid_to / patternPrevious value: -"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"New value: +"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$" - added
Input schema / properties / verification_levelAdded value: +{ + "enum": [ + "declared", + "present", + "hash_verified", + "restore_tested", + "replayed" + ], + "type": "string" +} - added
Input schema / properties / wait_timeout_msAdded value: +{ + "maximum": 60000, + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / contextAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / currentAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / diffAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / doctorAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / error / properties / cancel_reasonAdded value: +{ + "type": "string" +} - added
Output schema / properties / error / properties / phaseAdded value: +{ + "type": "string" +} - added
Output schema / properties / error / properties / request_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / groupsAdded value: +{ + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / importedAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / jobAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / query_statusAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / receiptAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / receiptsAdded value: +{ + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / recordsAdded value: +{ + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / body_handleAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / index_freshnessAdded value: +{ + "enum": [ + "current", + "stale" + ], + "type": "string" +} - added
Output schema / properties / results / items / properties / record_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / revision_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / source_spanAdded value: +{ + "additionalProperties": false, + "properties": { + "end": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "start": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "start", + "end" + ], + "type": "object" +} - added
Output schema / properties / snapshotAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / timelineAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / verificationAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +}
- Changed
project_memory_search29 fields changed- added
Input schema / properties / filtersAdded value: +{ + "description": "Optional project, source, type, role, lane, and recorded-time filters. Excluded lanes are not executed.", + "properties": { + "kinds": { + "items": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "maxItems": 20, + "type": "array" + }, + "recorded_after": { + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "recorded_before": { + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "retrieval_lanes": { + "items": { + "enum": [ + "exact", + "bm25", + "vector", + "graph" + ], + "type": "string" + }, + "maxItems": 4, + "type": "array" + }, + "roles": { + "items": { + "enum": [ + "experiment_record", + "result", + "method", + "log", + "paper", + "plan", + "config", + "source", + "report", + "other" + ], + "type": "string" + }, + "maxItems": 10, + "type": "array" + }, + "source_families": { + "items": { + "enum": [ + "document", + "code", + "graph" + ], + "type": "string" + }, + "maxItems": 3, + "type": "array" + } + }, + "type": "object" +} - added
Input schema / properties / queriesAdded value: +{ + "description": "Independent questions returned as separate ranked groups inside one broker request.", + "items": { + "properties": { + "id": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "limit": { + "maximum": 20, + "minimum": 1, + "type": "integer" + }, + "query": { + "maxLength": 400, + "minLength": 1, + "type": "string" + }, + "query_variants": { + "items": { + "maxLength": 400, + "minLength": 1, + "type": "string" + }, + "maxItems": 19, + "type": "array" + } + }, + "required": [ + "id", + "query" + ], + "type": "object" + }, + "maxItems": 20, + "minItems": 1, + "type": "array" +} - changed
Input schema / properties / query / descriptionPrevious value: -"Natural-language query over indexed local project knowledge."New value: +"One natural-language query over indexed local project knowledge. Use queries instead for independent grouped answers." - added
Input schema / properties / request_idAdded value: +{ + "description": "Caller-chosen ID for one local query. Supply it before a long request so project_memory query_status/query_cancel can inspect or cancel the same operation.", + "maxLength": 200, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / response_budget_bytesAdded value: +{ + "description": "Explicit result-array byte budget. Without an override, summaries adapt to unique result, question and source-family counts. Stored bodies and retrieval scope are not truncated by this budget; envelope metadata is separate.", + "maximum": 1000000, + "minimum": 1000, + "type": "integer" +} - added
Input schema / properties / response_deadline_msAdded value: +{ + "description": "Caller-selected 1-300 second response deadline. Returns available lane results or QUERY_TIMEOUT with request_id. Native reads remain tracked until they settle; stored evidence and writes are unaffected.", + "maximum": 300000, + "minimum": 1000, + "type": "integer" +} - removed
Input schema / requiredRemoved value: -[ - "query" -] - added
Output schema / properties / contextAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / currentAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / diffAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / doctorAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / error / properties / cancel_reasonAdded value: +{ + "type": "string" +} - added
Output schema / properties / error / properties / phaseAdded value: +{ + "type": "string" +} - added
Output schema / properties / error / properties / request_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / groupsAdded value: +{ + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / importedAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / jobAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / query_statusAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / receiptAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / receiptsAdded value: +{ + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / recordsAdded value: +{ + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / body_handleAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / index_freshnessAdded value: +{ + "enum": [ + "current", + "stale" + ], + "type": "string" +} - added
Output schema / properties / results / items / properties / record_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / revision_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / source_spanAdded value: +{ + "additionalProperties": false, + "properties": { + "end": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "start": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "start", + "end" + ], + "type": "object" +} - added
Output schema / properties / snapshotAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / timelineAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / verificationAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +}
- Changed
scholar_search3 fields changed- added
Output schema / properties / error / properties / cancel_reasonAdded value: +{ + "type": "string" +} - added
Output schema / properties / error / properties / phaseAdded value: +{ + "type": "string" +} - added
Output schema / properties / error / properties / request_idAdded value: +{ + "type": "string" +}
- Changed
search8 fields changed- added
Output schema / properties / error / properties / cancel_reasonAdded value: +{ + "type": "string" +} - added
Output schema / properties / error / properties / phaseAdded value: +{ + "type": "string" +} - added
Output schema / properties / error / properties / request_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / body_handleAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / index_freshnessAdded value: +{ + "enum": [ + "current", + "stale" + ], + "type": "string" +} - added
Output schema / properties / results / items / properties / record_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / revision_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / source_spanAdded value: +{ + "additionalProperties": false, + "properties": { + "end": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "start": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "start", + "end" + ], + "type": "object" +}
- Changed
search_parallel8 fields changed- added
Output schema / properties / error / properties / cancel_reasonAdded value: +{ + "type": "string" +} - added
Output schema / properties / error / properties / phaseAdded value: +{ + "type": "string" +} - added
Output schema / properties / error / properties / request_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / body_handleAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / index_freshnessAdded value: +{ + "enum": [ + "current", + "stale" + ], + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / record_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / revision_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / source_spanAdded value: +{ + "additionalProperties": false, + "properties": { + "end": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "start": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "start", + "end" + ], + "type": "object" +}
3 tool updates
v1.0.6- Changed
project_memory4 fields changed- added
Input schema / properties / detail_levelAdded value: +{ + "default": "summary", + "description": "For show with project_id and no target_id, both values return a bounded summary. full is retained only for compatibility and never returns every durable record body.", + "enum": [ + "summary", + "full" + ], + "type": "string" +} - added
Output schema / properties / decision_countAdded value: +{ + "type": "number" +} - added
Output schema / properties / experiment_countAdded value: +{ + "type": "number" +} - added
Output schema / properties / plan_countAdded value: +{ + "type": "number" +}
- Changed
project_memory_search3 fields changed- added
Output schema / properties / decision_countAdded value: +{ + "type": "number" +} - added
Output schema / properties / experiment_countAdded value: +{ + "type": "number" +} - added
Output schema / properties / plan_countAdded value: +{ + "type": "number" +}
- Changed
search2 fields changed- changed
Input schema / properties / response_content / defaultPrevious value: -"full"New value: +"summary" - changed
Input schema / properties / response_content / descriptionPrevious value: -"Controls only the returned body. full returns up to max_chars; summary returns a 1500-character evidence excerpt. Research storage keeps the full captured text in deterministic chunks."New value: +"Controls only the returned body. summary is the default and returns a 1500-character evidence excerpt; full returns up to max_chars. Research storage keeps the full captured text in deterministic chunks."
5 tool updates
v1.0.4- Changed
health1 field changed- added
Output schema / properties / nativeBrowserAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +}
- Changed
project_memory1 field changed- added
Input schema / properties / query_variantsAdded value: +{ + "description": "Optional local retrieval variants for action=search. They run inside one broker request and are fused before one rerank.", + "items": { + "maxLength": 400, + "minLength": 1, + "type": "string" + }, + "maxItems": 19, + "type": "array" +}
- Changed
project_memory_search1 field changed- added
Input schema / properties / query_variantsAdded value: +{ + "description": "Optional retrieval variants executed inside this one broker request. Exact identifiers and quoted phrases are added deterministically, candidates are fused with RRF, and the primary query is reranked once. Do not make repeated terminal or tool calls for query variants.", + "items": { + "maxLength": 400, + "minLength": 1, + "type": "string" + }, + "maxItems": 19, + "type": "array" +}
- Changed
search1 field changed- changed
Input schema / properties / extract_mode / descriptionPrevious value: -"Content depth in this search call. Use abstract or full during research instead of following search with separate extract calls. For GitHub results, none reads the README; abstract and full use the same repository eligibility gate but index different source amounts."New value: +"Content depth in this search call. Use none only when titles and snippets are enough, abstract for relevance evidence, and full when the user asks to read originals, full text, document bodies, or code, or to compare source contents. Use abstract or full here instead of following search with separate extraction, PDF download, or repository clone. For GitHub results, none reads the README; abstract and full use the same repository eligibility gate but index different source amounts."
- Changed
search_parallel1 field changed- changed
Input schema / properties / extract_mode / descriptionPrevious value: -"Content depth in this search call. Use abstract or full during research instead of following search with separate extract calls. For GitHub results, none reads the README; abstract and full use the same repository eligibility gate but index different source amounts."New value: +"Content depth in this search call. Use none only when titles and snippets are enough, abstract for relevance evidence, and full when the user asks to read originals, full text, document bodies, or code, or to compare source contents. Use abstract or full here instead of following search with separate extraction, PDF download, or repository clone. For GitHub results, none reads the README; abstract and full use the same repository eligibility gate but index different source amounts."
6 tool updates
v1.0.2- Changed
extract4 fields changed- changed
Input schema / properties / max_chars / descriptionPrevious value: -"Truncate body to this many chars. Defaults to 1500 for abstract and 50000 for full."New value: +"Maximum returned characters when response_content=full. Defaults to 1500 for abstract and 50000 for full." - added
Input schema / properties / response_contentAdded value: +{ + "default": "full", + "description": "Controls only the returned body. full returns up to max_chars; summary returns a 1500-character evidence excerpt. Research storage keeps the full captured text in deterministic chunks.", + "enum": [ + "summary", + "full" + ], + "type": "string" +} - added
Output schema / properties / source_lengthAdded value: +{ + "type": "number" +} - added
Output schema / properties / truncatedAdded value: +{ + "type": "boolean" +}
- Changed
project_memory2 fields changed- added
Output schema / properties / results / items / properties / source_lengthAdded value: +{ + "type": "number" +} - added
Output schema / properties / results / items / properties / truncatedAdded value: +{ + "type": "boolean" +}
- Changed
project_memory_search2 fields changed- added
Output schema / properties / results / items / properties / source_lengthAdded value: +{ + "type": "number" +} - added
Output schema / properties / results / items / properties / truncatedAdded value: +{ + "type": "boolean" +}
- Changed
scholar_search1 field changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Max papers (default 10)."New value: +"Maximum papers. Integer 1-10, default 10."
- Changed
search6 fields changed- changed
Input schema / properties / extract_limit / descriptionPrevious value: -"Maximum unique result URLs to extract. For parallel search this is one call-wide limit, not a per-query limit."New value: +"Maximum unique result URLs to extract. Integer 1-10, default 5." - changed
Input schema / properties / limit / descriptionPrevious value: -"Max results (default 10)."New value: +"Maximum results. Integer 1-20, default 10." - changed
Input schema / properties / max_chars / descriptionPrevious value: -"Maximum characters per extracted result. Defaults to 1500 for abstract and 50000 for full."New value: +"Maximum returned characters per extracted result in response_content=full. Defaults to 1500 for abstract and 50000 for full." - added
Input schema / properties / response_contentAdded value: +{ + "default": "full", + "description": "Controls only the returned body. full returns up to max_chars; summary returns a 1500-character evidence excerpt. Research storage keeps the full captured text in deterministic chunks.", + "enum": [ + "summary", + "full" + ], + "type": "string" +} - added
Output schema / properties / results / items / properties / source_lengthAdded value: +{ + "type": "number" +} - added
Output schema / properties / results / items / properties / truncatedAdded value: +{ + "type": "boolean" +}
- Changed
search_parallel8 fields changed- removed
Input schema / properties / extract_limit / defaultRemoved value: -5 - changed
Input schema / properties / extract_limit / descriptionPrevious value: -"Maximum unique result URLs to extract. For parallel search this is one call-wide limit, not a per-query limit."New value: +"Call-wide maximum unique result URLs to extract. Integer 1-20, default 12 for abstract; full defaults to and allows at most 10." - changed
Input schema / properties / extract_limit / maximumPrevious value: -10New value: +20 - changed
Input schema / properties / limit / descriptionPrevious value: -"Max results per query."New value: +"Maximum results per query. Integer 1-20, default 10." - changed
Input schema / properties / max_chars / descriptionPrevious value: -"Maximum characters per extracted result. Defaults to 1500 for abstract and 50000 for full."New value: +"Maximum returned characters per extracted result in response_content=full. Defaults to 1500 for abstract and 50000 for full." - added
Input schema / properties / response_contentAdded value: +{ + "default": "summary", + "description": "Controls only returned bodies. summary is the default and returns 1500-character evidence excerpts; full returns up to max_chars. Research storage keeps the full captured text in deterministic chunks.", + "enum": [ + "summary", + "full" + ], + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / source_lengthAdded value: +{ + "type": "number" +} - added
Output schema / properties / results / items / properties / results / items / properties / truncatedAdded value: +{ + "type": "boolean" +}
8 tool updates
v1.0.0- Changed
extract23 fields changed- removed
Input schema / properties / max_chars / defaultRemoved value: -8000 - changed
Input schema / properties / max_chars / descriptionPrevious value: -"Truncate body to this many chars (default 8000, set via SURF_EXTRACT_MAX_CHARS)."New value: +"Truncate body to this many chars. Defaults to 1500 for abstract and 50000 for full." - added
Input schema / properties / memory_handleAdded value: +{ + "description": "Reuse the handle returned by a prior project-aware call.", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$", + "type": "string" +} - changed
Input schema / properties / mode / descriptionPrevious value: -"Extraction depth. `full` = whole article body (default; uses Playwright if needed). `abstract` = cheap survey: PDF page 1 OR HTML meta description (~1500 chars); use to triage relevance before paying for full text. `metadata` = page count only (PDF). Academic PDFs (arxiv/biorxiv/Nature/OpenReview/NeurIPS/JMLR/PMLR/Springer/PubMed-via-PMC) are auto-detected; abstract mode skips Playwright for them."New value: +"Extraction depth. `full` = whole article body (default; uses Playwright if needed). `abstract` = cheap survey: PDF page 1 OR HTML meta description (~1500 chars); use to triage relevance before paying for full text. `metadata` = document metadata without body text: title, authors, publication details, dates, DOI, keywords, canonical URL, and PDF properties when available. Academic PDFs (arxiv/biorxiv/Nature/OpenReview/NeurIPS/JMLR/PMLR/Springer/PubMed-via-PMC) are auto-detected; abstract mode skips Playwright for them." - added
Input schema / properties / project_idAdded value: +{ + "description": "Project memory id.", + "maxLength": 64, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / session_idAdded value: +{ + "description": "Stable host task id. Reuses the same project session after restart.", + "maxLength": 200, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / session_intentAdded value: +{ + "description": "Current durable task intent. A changed value creates an immutable revision.", + "maxLength": 2000, + "minLength": 1, + "type": "string" +} - added
Output schema / properties / authorsAdded value: +{ + "type": "string" +} - added
Output schema / properties / canonical_urlAdded value: +{ + "type": "string" +} - added
Output schema / properties / created_atAdded value: +{ + "type": "string" +} - added
Output schema / properties / creatorAdded value: +{ + "type": "string" +} - added
Output schema / properties / descriptionAdded value: +{ + "type": "string" +} - added
Output schema / properties / doiAdded value: +{ + "type": "string" +} - added
Output schema / properties / keywordsAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / languageAdded value: +{ + "type": "string" +} - added
Output schema / properties / memoryAdded value: +{ + "type": "string" +} - added
Output schema / properties / memory_handleAdded value: +{ + "type": "string" +} - added
Output schema / properties / modified_atAdded value: +{ + "type": "string" +} - added
Output schema / properties / producerAdded value: +{ + "type": "string" +} - added
Output schema / properties / publicationAdded value: +{ + "type": "string" +} - added
Output schema / properties / published_atAdded value: +{ + "type": "string" +} - added
Output schema / properties / subjectAdded value: +{ + "type": "string" +} - added
Output schema / properties / yearAdded value: +{ + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +}
- Changed
health1 field changed- added
Output schema / properties / researchAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +}
- Added
project_memory - Added
project_memory_search - Changed
scholar_search7 fields changed- added
Input schema / properties / memory_handleAdded value: +{ + "description": "Reuse the handle returned by a prior project-aware call.", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$", + "type": "string" +} - added
Input schema / properties / project_idAdded value: +{ + "description": "Project memory id.", + "maxLength": 64, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / session_idAdded value: +{ + "description": "Stable host task id. Reuses the same project session after restart.", + "maxLength": 200, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / session_intentAdded value: +{ + "description": "Current durable task intent. A changed value creates an immutable revision.", + "maxLength": 2000, + "minLength": 1, + "type": "string" +} - added
Output schema / properties / memoryAdded value: +{ + "type": "string" +} - added
Output schema / properties / memory_handleAdded value: +{ + "type": "string" +} - added
Output schema / properties / research_contextAdded value: +{ + "additionalProperties": false, + "properties": { + "prior_searches": { + "items": { + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + }, + "relation": { + "enum": [ + "same", + "related", + "recent" + ], + "type": "string" + }, + "results": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "searched_at": { + "type": "string" + }, + "surface": { + "type": "string" + } + }, + "required": [ + "query", + "relation", + "searched_at", + "results" + ], + "type": "object" + }, + "maxItems": 3, + "type": "array" + } + }, + "required": [ + "prior_searches" + ], + "type": "object" +}
- Changed
search37 fields changed- added
Input schema / properties / extract_limitAdded value: +{ + "default": 5, + "description": "Maximum unique result URLs to extract. For parallel search this is one call-wide limit, not a per-query limit.", + "maximum": 10, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / extract_modeAdded value: +{ + "default": "none", + "description": "Content depth in this search call. Use abstract or full during research instead of following search with separate extract calls. For GitHub results, none reads the README; abstract and full use the same repository eligibility gate but index different source amounts.", + "enum": [ + "none", + "abstract", + "full" + ], + "type": "string" +} - added
Input schema / properties / include_project_idsAdded value: +{ + "description": "Additional read-only projects joined through ontology-aligned schema and identity links. New records stay in project_id.", + "items": { + "maxLength": 64, + "minLength": 1, + "type": "string" + }, + "maxItems": 8, + "type": "array" +} - added
Input schema / properties / max_charsAdded value: +{ + "description": "Maximum characters per extracted result. Defaults to 1500 for abstract and 50000 for full.", + "maximum": 50000, + "minimum": 200, + "type": "integer" +} - added
Input schema / properties / memory_handleAdded value: +{ + "description": "Reuse the handle returned by a prior project-aware call.", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$", + "type": "string" +} - added
Input schema / properties / project_idAdded value: +{ + "description": "Project memory id.", + "maxLength": 64, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / session_idAdded value: +{ + "description": "Stable host task id. Reuses the same project session after restart.", + "maxLength": 200, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / session_intentAdded value: +{ + "description": "Current durable task intent. A changed value creates an immutable revision.", + "maxLength": 2000, + "minLength": 1, + "type": "string" +} - added
Output schema / properties / memoryAdded value: +{ + "type": "string" +} - added
Output schema / properties / memory_handleAdded value: +{ + "type": "string" +} - added
Output schema / properties / research_contextAdded value: +{ + "additionalProperties": false, + "properties": { + "prior_searches": { + "items": { + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + }, + "relation": { + "enum": [ + "same", + "related", + "recent" + ], + "type": "string" + }, + "results": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "searched_at": { + "type": "string" + }, + "surface": { + "type": "string" + } + }, + "required": [ + "query", + "relation", + "searched_at", + "results" + ], + "type": "object" + }, + "maxItems": 3, + "type": "array" + } + }, + "required": [ + "prior_searches" + ], + "type": "object" +} - added
Output schema / properties / results / items / properties / authorsAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / canonical_urlAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / contentAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / created_atAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / creatorAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / document_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / doiAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / excerptAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / extract_errorAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / extraction_qualityAdded value: +{ + "enum": [ + "full_text", + "abstract", + "meta_abstract", + "metadata_only" + ], + "type": "string" +} - added
Output schema / properties / results / items / properties / fresh_webAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / results / items / properties / is_pdfAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / results / items / properties / keywordsAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / languageAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / lengthAdded value: +{ + "type": "number" +} - added
Output schema / properties / results / items / properties / modified_atAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / page_countAdded value: +{ + "type": "number" +} - added
Output schema / properties / results / items / properties / producerAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / project_idsAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / publicationAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / published_atAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / retrieval_familiesAdded value: +{ + "items": { + "enum": [ + "exact", + "bm25", + "vector", + "graph" + ], + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / scoreAdded value: +{ + "type": "number" +} - added
Output schema / properties / results / items / properties / source_familyAdded value: +{ + "enum": [ + "live", + "document", + "code", + "graph" + ], + "type": "string" +} - added
Output schema / properties / results / items / properties / subjectAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / yearAdded value: +{ + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +}
- Removed
search_extract - Changed
search_parallel43 fields changed- added
Input schema / properties / extract_limitAdded value: +{ + "default": 5, + "description": "Maximum unique result URLs to extract. For parallel search this is one call-wide limit, not a per-query limit.", + "maximum": 10, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / extract_modeAdded value: +{ + "default": "none", + "description": "Content depth in this search call. Use abstract or full during research instead of following search with separate extract calls. For GitHub results, none reads the README; abstract and full use the same repository eligibility gate but index different source amounts.", + "enum": [ + "none", + "abstract", + "full" + ], + "type": "string" +} - added
Input schema / properties / include_project_idsAdded value: +{ + "description": "Additional read-only projects joined through ontology-aligned schema and identity links. New records stay in project_id.", + "items": { + "maxLength": 64, + "minLength": 1, + "type": "string" + }, + "maxItems": 8, + "type": "array" +} - added
Input schema / properties / max_charsAdded value: +{ + "description": "Maximum characters per extracted result. Defaults to 1500 for abstract and 50000 for full.", + "maximum": 50000, + "minimum": 200, + "type": "integer" +} - added
Input schema / properties / memory_handleAdded value: +{ + "description": "Reuse the handle returned by a prior project-aware call.", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$", + "type": "string" +} - added
Input schema / properties / project_idAdded value: +{ + "description": "Project memory id.", + "maxLength": 64, + "minLength": 1, + "type": "string" +} - changed
Input schema / properties / queries / descriptionPrevious value: -"2-10 queries to run concurrently."New value: +"2-12 independent live queries." - added
Input schema / properties / queries / items / maxLengthAdded value: +400 - added
Input schema / properties / queries / items / minLengthAdded value: +1 - changed
Input schema / properties / queries / maxItemsPrevious value: -10New value: +12 - changed
Input schema / properties / queries / minItemsPrevious value: -1New value: +2 - added
Input schema / properties / session_idAdded value: +{ + "description": "Stable host task id. Reuses the same project session after restart.", + "maxLength": 200, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / session_intentAdded value: +{ + "description": "Current durable task intent. A changed value creates an immutable revision.", + "maxLength": 2000, + "minLength": 1, + "type": "string" +} - added
Output schema / properties / memoryAdded value: +{ + "type": "string" +} - added
Output schema / properties / memory_handleAdded value: +{ + "type": "string" +} - added
Output schema / properties / research_contextAdded value: +{ + "additionalProperties": false, + "properties": { + "prior_searches": { + "items": { + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + }, + "relation": { + "enum": [ + "same", + "related", + "recent" + ], + "type": "string" + }, + "results": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "searched_at": { + "type": "string" + }, + "surface": { + "type": "string" + } + }, + "required": [ + "query", + "relation", + "searched_at", + "results" + ], + "type": "object" + }, + "maxItems": 3, + "type": "array" + } + }, + "required": [ + "prior_searches" + ], + "type": "object" +} - changed
Output schema / properties / results / items / properties / provider / enumPrevious value: -[ - "browser", - "searchapi" -]New value: +[ + "browser", + "searchapi", + "local" +] - added
Output schema / properties / results / items / properties / results / items / properties / authorsAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / canonical_urlAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / contentAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / created_atAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / creatorAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / document_idAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / doiAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / excerptAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / extract_errorAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / extraction_qualityAdded value: +{ + "enum": [ + "full_text", + "abstract", + "meta_abstract", + "metadata_only" + ], + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / fresh_webAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / results / items / properties / results / items / properties / is_pdfAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / results / items / properties / results / items / properties / keywordsAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / results / items / properties / languageAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / lengthAdded value: +{ + "type": "number" +} - added
Output schema / properties / results / items / properties / results / items / properties / modified_atAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / page_countAdded value: +{ + "type": "number" +} - added
Output schema / properties / results / items / properties / results / items / properties / producerAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / project_idsAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / results / items / properties / publicationAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / published_atAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / retrieval_familiesAdded value: +{ + "items": { + "enum": [ + "exact", + "bm25", + "vector", + "graph" + ], + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / results / items / properties / scoreAdded value: +{ + "type": "number" +} - added
Output schema / properties / results / items / properties / results / items / properties / source_familyAdded value: +{ + "enum": [ + "live", + "document", + "code", + "graph" + ], + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / subjectAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / results / items / properties / yearAdded value: +{ + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +}
3 tool updates
v0.8.0- Added
scholar_search - Changed
search_extract1 field changed- changed
Input schema / properties / max_chars / descriptionPrevious value: -"Truncate each result body. Default depends on mode: ~1500 for abstract, 8000 for full."New value: +"Truncate each result body. Default depends on mode: ~1500 for abstract, 8000 for full (SURF_EXTRACT_MAX_CHARS, capped at 20000 here)."
- Changed
search_parallel3 fields changed- added
Output schema / properties / results / items / properties / degraded_reasonsAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / fallback_reasonAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / providerAdded value: +{ + "enum": [ + "browser", + "searchapi" + ], + "type": "string" +}
1 tool update
v0.6.8- Changed
extract1 field changed- changed
Input schema / properties / max_chars / descriptionPrevious value: -"Truncate body to this many chars (default 8000)."New value: +"Truncate body to this many chars (default 8000, set via SURF_EXTRACT_MAX_CHARS)."
5 tool updates
v0.6.5- Added
extract - Added
health - Added
search - Added
search_extract - Added
search_parallel
5 tool updates
v0.6.1- Removed
extract - Removed
health - Removed
search - Removed
search_extract - Removed
search_parallel
1 tool update
v0.5.6- Changed
health3 fields changed- added
Output schema / properties / poolAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / selfHealingAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / telemetryAdded value: +{ + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" +}
5 tool updates
v0.5.2- Added
extract - Added
health - Added
search - Added
search_extract - Added
search_parallel
5 tool updates
v0.5.1- Removed
extract - Removed
health - Removed
search - Removed
search_extract - Removed
search_parallel
5 tool updates
v0.4.8- Added
extract - Added
health - Added
search - Added
search_extract - Added
search_parallel
TDQS
Scored across 7 tools
Each tool has a clearly scoped role: search for single-query live lookup, search_parallel for multi-query lookup, extract for known-URL reading, scholar_search for paper metadata, and project_memory_search for local-only retrieval. Even potential overlaps like search's extract_mode vs extract are explicitly separated by whether discovery is still needed.
Naming is reasonably readable but not consistent: some tools are bare verbs (search, extract), some are noun+search (scholar_search, project_memory_search), and health is a standalone noun while project_memory is a broad domain name. There is no uniform verb_noun or noun_verb convention across the surface.
Seven tools is well-scoped for the server's purpose: live web search, parallel search, extraction, scholar metadata, local memory search, memory management, and health. Each tool occupies a distinct place, and the count is within the ideal range without redundant or missing entries.
The surface covers the full workflow: discover via search, extract via extract or search mode, search scholar metadata, retrieve from local project memory, and manage that memory's full lifecycle. Project_memory is especially thorough, offering writes, updates, timelines, deletion, rollback, snapshots, and diagnostics, so agents should not hit dead ends.
Maintenance
Related MCP Connectors
MCP server for Google search results via SERP API
One MCP for the Web. Easily search, crawl, navigate, and extract websites without getting blocked.…
Official SerpApi MCP server for Google, Bing, and other search engines.
Serper MCP — wraps the Serper Google Search API (serper.dev)
Related MCP Servers
- AlicenseNot gradedqualityNot gradedmaintenanceProvides Google search capabilities, web content extraction, and screenshot functionality with advanced bot detection avoidance through the MCP protocol.16 npm6-
- AlicenseBqualityFmaintenanceA powerful MCP server that enables parallel Google searching with multiple keywords simultaneously, providing structured results while handling CAPTCHAs and simulating user browsing patterns.171 npm273MIT
- MIT

Brave Search MCP Serverofficial
AlicenseAqualityAmaintenanceAn MCP implementation that integrates the Brave Search API, providing comprehensive search capabilities including web, local business, image, video, news searches, and AI-powered summarization.8822,398 npm1,471MIT