# web-search-plus-mcp - Doramagic AI Context Pack

> Positioning: a pre-install experience and judgment asset. It helps the host AI get off to a good start, but it does not mean the project has already been installed, run, or validated.

## Sufficiency Principle

- **Sufficiency over compression**: The AI Context Pack should be sufficient for the host AI to understand the project's value, capability boundaries, entrypoints, risks, and evidence sources before starting work; it may be layered, but it does not aim for the shortest possible summary.
- **Compression policy**: Compress only noise and duplication, never context that affects judgment or the quality of the work.

## How the Host AI Should Use This

You are reading the AI Context Pack that Doramagic compiled for web-search-plus-mcp. Treat it as pre-work context: help the user understand who it fits, what it can do, how to start, what must be verified after install, and where the risks are. Do not claim that you have already installed, run, or executed the target project.

## Claim Consumption Rules

- **Fact source**: Repo Evidence + Claim/Evidence Graph; the Human Wiki only supplies salience, terminology, and narrative structure.
- **Minimum status for a fact**: `supported`
- `supported`: May be used as a project fact, but the answer must cite the claim_id and evidence path.
- `weak`: Usable only as a low-confidence lead; the user must be asked to keep verifying.
- `inferred`: Usable only for risk notes or open questions; must not be packaged as a project fact.
- `unverified`: Must not be used as fact; state clearly that evidence is insufficient.
- `contradicted`: Must show the conflicting sources and must not force a single version on the user's behalf.

## Who It Fits Best

- **Developers already using host AIs such as Claude/Codex/Cursor/Gemini**: The README or plugin config mentions multiple host AIs. Evidence: `README.md` Claim: `clm_0002` supported 0.86

## What It Can Do

- **Command-Line Startup or Install Flow** (Verify after install): The project documentation contains runnable commands; real use requires running them in a local or host environment. Evidence: `README.md` Claim: `clm_0001` supported 0.86

## How to Start

- `pip install web-search-plus-mcp` Evidence: `README.md` Claim: `clm_0003` supported 0.86

## Continue-or-Stop Decision Card

- **Current recommendation**: Needs admin / security approval
- **Why**: Continuing may involve secrets, accounts, external services, or sensitive context; get admin or security approval first.

### 30-Second Read

- **What to do now**: Needs admin / security approval
- **Minimum safe next step**: Run Prompt Preview first; if credentials or an enterprise environment are involved, get approval before trialing
- **Do not trust yet**: Tool permission boundaries cannot be trusted before install.
- **Continuing will touch**: Command execution, Local environment or project files, Environment variables / API keys

### What You Can Trust Now

- **Target-audience signal: Developers already using host AIs such as Claude/Codex/Cursor/Gemini** (supported): Backed by a supported claim or project evidence, but that still is not the same as real install results. Evidence: `README.md` Claim: `clm_0002` supported 0.86
- **Capability exists: Command-Line Startup or Install Flow** (supported): You can trust that the project contains signals of this capability; whether it fits your specific task still needs trial or after-install verification. Evidence: `README.md` Claim: `clm_0001` supported 0.86
- **There are Quick Start / install-command signals** (supported): You can trust that the docs mention a startup or install entrypoint; do not run it directly in your primary environment because of that. Evidence: `README.md` Claim: `clm_0003` supported 0.86

### What You Cannot Trust Yet

- **Tool permission boundaries cannot be trusted before install.** (unverified): MCP/tool projects usually touch files, the network, the browser, or external APIs, so permissions and logs must be checked for real.
- **Real output quality cannot be trusted before install.** (unverified): Prompt Preview can only show how it guides you; it cannot prove result quality in the real project.
- **Host AI version compatibility cannot be trusted before install.** (unverified): Host loading rules and version differences across Claude, Cursor, Codex, Gemini, and others must be verified in a real environment.
- **That it will not pollute your existing host AI's behavior cannot be trusted directly.** (inferred): Skill, plugin, and AGENTS/CLAUDE/GEMINI instructions may change the host AI's default behavior.
- **Safe rollback cannot be assumed by default.** (unverified): Unless the project clearly provides uninstall and recovery instructions, verify in an isolated environment first.
- **After a real install, is it compatible with the user's current host AI version?** (unverified): Compatibility can only be verified in the actual host environment.
- **Does the project's output quality meet the user's specific task?** (unverified): The pre-install preview can only show flow and boundaries; it cannot replace real evaluation.
- **Do the install commands require network access, permissions, or global writes?** (unverified): This affects install risk in both enterprise and personal environments. Evidence: `README.md`

### What Continuing Will Touch

- **Command execution**: Package managers, network downloads, the local plugin directory, project config, or the user's home directory. Why: Running the very first command can already change your environment; decide whether it is worth running first. Evidence: `README.md`
- **Local environment or project files**: Install results, plugin caches, project config, or local dependency directories. Why: The write scope and rollback path cannot be proven before install and need isolated verification. Evidence: `README.md`
- **Environment variables / API keys**: Project entry docs explicitly showing API key, token, secret, or account credential configuration. Why: If a real install needs credentials, use test credentials first and go through a permission/compliance review. Evidence: `CHANGELOG.md`, `README.md`
- **Host AI context**: The AI Context Pack, Prompt Preview, Skill routing, risk rules, and project facts. Why: Importing context affects the host AI's later judgment, so avoid packaging unverified items as facts.

### Minimum Safe Next Steps

- **Run Prompt Preview first**: Use a pre-install interactive trial to judge whether the way of working fits; it needs no authorization or environment change. (applies when: Applies to any project, especially when output quality is unknown.)
- **Trial-install only in an isolated directory or a test account**: Avoid letting install commands pollute your primary host AI, real projects, or home directory. (applies when: When there are signals of command execution, plugin config, or local writes.)
- **Do not use real production credentials**: Once an environment variable / API key enters the host or toolchain, it can create account and compliance risk. (applies when: When environment signals like API, TOKEN, KEY, or SECRET appear.)
- **After install, verify just one minimal task**: Verify loading, compatibility, output quality, and rollback first, then decide whether to use it deeply. (applies when: When moving from a trial into a real workflow.)

### Exit Plan

- **Preserve the pre-install state**: Record the original host config and project state so you can later judge whether it is recoverable.
- **Record the install commands and written paths**: Without clear uninstall instructions, you at least need to know which directories or configs to clean up manually.
- **Be ready to revoke test API keys or tokens**: If test credentials leak or are misused, you can cut losses quickly.
- **If there is no rollback path, do not enter your primary environment**: No rollback is a blocker before continuing; do not proceed on trust or luck.

## What Can Only Be Previewed

- Explain who the project fits and what it can do
- Demonstrate a typical conversation flow based on project docs
- Help the user decide whether it is worth installing or researching further

## What Must Be Verified After Install

- Actually installing the Skill, plugin, or CLI
- Running scripts, modifying local files, or accessing external services
- Verifying real output quality, performance, and compatibility

## Boundary & Risk Decision Card

- **Mistaking the pre-install preview for a real run**: The user may overestimate how much configuration, permission, and compatibility verification the project has already done. Mitigation: Clearly separate prompt_preview_can_do from runtime_required. Claim: `clm_0004` inferred 0.45
- **Command execution will modify the local environment**: Install commands may write to the user's home directory, the host plugin directory, or project configuration. Mitigation: Run in an isolated environment or a test account first. Evidence: `README.md` Claim: `clm_0005` supported 0.86
- **To confirm**: After a real install, is it compatible with the user's current host AI version?. Why: Compatibility can only be verified in the actual host environment.
- **To confirm**: Does the project's output quality meet the user's specific task?. Why: The pre-install preview can only show flow and boundaries; it cannot replace real evaluation.
- **To confirm**: Do the install commands require network access, permissions, or global writes?. Why: This affects install risk in both enterprise and personal environments.

## Pre-Work Working Context

### Loading Order

- First read how_to_use.host_ai_instruction to establish the boundaries of this pre-install judgment asset.
- Read claim_graph_summary to confirm facts come from the Claim/Evidence Graph, not the Human Wiki narrative.
- Then read intended_users, capabilities, and quick_start_candidates to judge whether the user is a match.
- When you need to carry out a concrete task, check role_skill_index first, then evidence_index.
- For real install, file modification, network access, performance, or compatibility questions, turn to risk_card and boundaries.runtime_required.

### Task Routes

- **Command-Line Startup or Install Flow**: State that this is an after-install capability first, then give a pre-install checklist. Boundary: Must be verified after a real install or run. Evidence: `README.md` Claim: `clm_0001` supported 0.86

### Context Scale

- Total files: 52
- Important-file coverage: 40/52
- Evidence index entries: 51
- Role / Skill entries: 3

### Handling Insufficient Evidence

- **missing_evidence**: State that evidence is insufficient and ask the user for the target file, a README section, or after-install verification records; do not fill in facts.
- **out_of_scope_request**: State that the task is beyond the current AI Context Pack's evidence scope and suggest the user check the Human Manual or verify after a real install.
- **runtime_request**: Provide a pre-install checklist and command sources, but do not run commands for the user or claim they have been run.
- **source_conflict**: Show the conflicting sources side by side, mark them as unverified, and do not force a single version.

## Prompt Recipes

### Fit assessment

- Goal: Judge whether this project fits the user's current task.
- Expected output: A fit conclusion, key reasons, evidence citations, what can be previewed before install, what must be verified after install, and a next-step recommendation.

```text
Based on the AI Context Pack for web-search-plus-mcp, ask me 3 necessary questions first, then judge whether it fits my task. The answer must cover: who it fits, what it can do, what it cannot do, whether it is worth installing, and where the evidence comes from. Every project fact must cite evidence_refs, source_paths, or a claim_id.
```

### Pre-install experience

- Goal: Let the user feel the core workflow before installing, while avoiding packaging the preview as real capability or a marketing promise.
- Expected output: An experience script with boundary labels, an after-install verification checklist, and a cautious recommendation; with no real-run promises or strong marketing language.

```text
Treat web-search-plus-mcp as a pre-install experience asset, not an already-installed tool or a real runtime environment.

Output exactly four parts:
1. Ask me 3 necessary questions first.
2. Give an "experience script": use the three labels [Previewable before install], [Must verify after install], and [Insufficient evidence] to show how it might guide the workflow.
3. Give an after-install verification checklist: list which capabilities can only be confirmed after a real install, real host loading, and a real project run.
4. Give a cautious recommendation: only "worth researching/trialing further", "add information before deciding", or "not recommended to continue"; do not endorse the project.

Hard boundaries:
- Do not claim you have installed, run, executed tests, modified files, or produced real results.
- Do not write promise-like phrasing such as "auto-adapts", "guarantees passing", "perfect fit", or "strongly recommend installing".
- If you describe how it works after install, you must use a conditional such as "if installed successfully and the host loads the Skill correctly, it might...".
- The experience script may only be written as "example lines / hypothetical flow": use "might ask / might suggest / might show", not "has written, has generated, has passed, is running, is generating".
- Prompt Preview does not hand out install commands; if the user is ready to trial, only prompt them to read Quick Start and the Risk Card first and to verify in an isolated environment.
- Every project fact must come from a supported claim, evidence_refs, or source_paths; inferred/unverified items can only be risks or open questions.

```

### Role / Skill selection

- Goal: Pick the best-matching asset from the project's roles or Skills.
- Expected output: A list of candidate roles or Skills, each with an applicable scenario, evidence paths, risk boundary, and whether after-install verification is needed.

```text
Read role_skill_index and recommend 3-5 of the most relevant roles or Skills for my target task. For each recommendation, state the applicable scenario, likely output, risk boundary, and evidence_refs.
```

### Risk pre-check

- Goal: Identify environment, permission, rule-conflict, and quality risks before installing or adopting.
- Expected output: A checklist of environment, permission, dependency, license, host-conflict, quality risk, and unknown items.

```text
Based on risk_card, boundaries, and quick_start_candidates, give me a pre-install risk pre-check list. Do not run commands for me; only explain what I should check, why, and what impact a failure would have.
```

### Host AI kickoff instruction

- Goal: Turn the project context into a host AI instruction for the start of a conversation.
- Expected output: A pre-work instruction with clear boundaries and clear evidence citations, suitable to copy to a host AI.

```text
Based on the AI Context Pack for web-search-plus-mcp, generate a pre-work instruction I can paste to my host AI. This instruction must obey not_runtime=true and must not claim the project has been installed, run, or produced real results.
```

## Role / Skill Index

- Indexed 3 role / Skill / project-doc entries.

- **🔍 web-search-plus-mcp** (project_doc): ! PyPI version https://img.shields.io/pypi/v/web-search-plus-mcp.svg https://pypi.org/project/web-search-plus-mcp/ ! Python 3.10+ https://img.shields.io/badge/python-3.10%2B-blue.svg https://www.python.org/downloads/ ! MCP https://img.shields.io/badge/MCP-compatible-green.svg https://modelcontextprotocol.io/ ! CI https://github.com/robbyczgw-cla/web-search-plus-mcp/actions/workflows/ci.yml/badge.svg https://github.c… Activation hint: Reference this when the user needs to understand the project's structure, install path, or boundaries. Evidence: `README.md`
- **Migrating web-search-plus-mcp 0.x to 1.0** (project_doc): Migrating web-search-plus-mcp 0.x to 1.0 Activation hint: Reference this when the user needs to understand the project's structure, install path, or boundaries. Evidence: `docs/MIGRATION_1_0.md`
- **Changelog** (project_doc): Added - Sync the portable Web Search Plus v3.1.1 policy layer: budget preflight, diversity scoring/reranking, self-hosted profiles, shadow-policy observations, semantic extraction spans, extraction cache identity v6, and SQLite state schema v3. - Add the public wsp sdk package plus fail-closed providers.d discovery, startup diagnostics, and network-free provider conformance checks. - Expose deterministic semantic sp… Activation hint: Reference this when the user needs to understand the project's structure, install path, or boundaries. Evidence: `CHANGELOG.md`

## Evidence Index

- Indexed 51 evidence entries.

- **🔍 web-search-plus-mcp** (documentation): ! PyPI version https://img.shields.io/pypi/v/web-search-plus-mcp.svg https://pypi.org/project/web-search-plus-mcp/ ! Python 3.10+ https://img.shields.io/badge/python-3.10%2B-blue.svg https://www.python.org/downloads/ ! MCP https://img.shields.io/badge/MCP-compatible-green.svg https://modelcontextprotocol.io/ ! CI https://github.com/robbyczgw-cla/web-search-plus-mcp/actions/workflows/ci.yml/badge.svg https://github.com/robbyczgw-cla/web-search-plus-mcp/actions/workflows/ci.yml ! License: MIT https://img.shields.io/badge/License-MIT-yellow.svg ./LICENSE ! Glama https://glama.ai/mcp/servers/robbyczgw-cla/web-search-plus-mcp/badge https://glama.ai/mcp/servers/robbyczgw-cla/web-search-plus-mcp Evidence: `README.md`
- **Migrating web-search-plus-mcp 0.x to 1.0** (documentation): Migrating web-search-plus-mcp 0.x to 1.0 Evidence: `docs/MIGRATION_1_0.md`
- **License** (source_file): Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files the "Software" , to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Evidence: `LICENSE`
- **Changelog** (documentation): Added - Sync the portable Web Search Plus v3.1.1 policy layer: budget preflight, diversity scoring/reranking, self-hosted profiles, shadow-policy observations, semantic extraction spans, extraction cache identity v6, and SQLite state schema v3. - Add the public wsp sdk package plus fail-closed providers.d discovery, startup diagnostics, and network-free provider conformance checks. - Expose deterministic semantic spans through the MCP web extract schema and CLI projection. Evidence: `CHANGELOG.md`
- **Pyproject** (source_file): build-system requires = "hatchling" build-backend = "hatchling.build" Evidence: `pyproject.toml`
- **Init** (source_file): PACKAGE DIR = str Path file .resolve .parent ⋮---- version = "1.1.0" Evidence: `web_search_plus_mcp/__init__.py`
- **A concurrent writer of the same immutable content won.** (source_file): DEFAULT MAX URLS = 10 HARD MAX URLS = 50 DEFAULT MAX CONTEXT CHARS = 60 000 MIN CONTEXT CHARS = 1 000 MAX CONTEXT CHARS = 200 000 DEFAULT FULL TEXT TTL SECONDS = 604 800 DEFAULT FULL TEXT MAX BYTES = 268 435 456 STORE NAME = "web text v3" MEDIA TYPE = "text/markdown" OWNED MARKER = " Dict str, Any : ... ⋮---- def integer option value: Any, name: str, default: int - int ⋮---- policy = config.get "bounded context" or {} ⋮---- policy = {} ⋮---- requested max urls = integer option operator max urls = integer option max urls = min ⋮---- operator default chars = integer option requested chars = integer option max context chars = min ⋮---- urls = list request.input "urls" processed urls = urls :ma… Evidence: `web_search_plus_mcp/bounded_context_v3.py`
- **Budget Preflight V3** (source_file): CHECK NAMES = ACTIONS = {"proceed", "degrade", "abort"} ON EXCEED = {"degrade", "abort"} ABORT REASONS = { ⋮---- @dataclass frozen=True class PreflightCheck ⋮---- check: str limit: int None observed: int None verdict: Literal "ok", "exceeded" ⋮---- def post init self - None ⋮---- def to dict self - Dict str, Any ⋮---- @dataclass frozen=True class PreflightDecision ⋮---- action: Literal "proceed", "degrade", "abort" checks: tuple PreflightCheck, ... = adjustments: Dict str, int = field default factory=dict reason: str None = None ⋮---- payload: Dict str, Any = { ⋮---- def section config: Mapping str, Any - Mapping str, Any ⋮---- section = config.get "budget preflight" or {} ⋮---- def limit s… Evidence: `web_search_plus_mcp/budget_preflight_v3.py`
- **Cache** (source_file): CACHE DIR = Path os.environ.get "WSP CACHE DIR", os.path.join os.path.dirname os.path.dirname os.path.abspath file , ".cache" DEFAULT CACHE TTL = 3600 PROVIDER HEALTH FILENAME = "provider health.json" ⋮---- WEB TEXT CACHE DIRNAME = "web" MAX STORED TEXT CHARS = 2 000 000 ⋮---- def atomic write text path: Path, text: str - None ⋮---- def get web text cache path url: str - Path ⋮---- key = hashlib.sha256 url.encode "utf-8" .hexdigest ⋮---- def iter web text cache files ⋮---- web dir = CACHE DIR / WEB TEXT CACHE DIRNAME ⋮---- def iter web text temp files ⋮---- def web text cache stats - Dict str, Any ⋮---- entries = total size = 0 ⋮---- def store web text url: str, text: str, max chars: int =… Evidence: `web_search_plus_mcp/cache.py`
- **Cache Identity V3** (source_file): EXTRACTION CACHE IDENTITY VERSION = 6 ⋮---- def canonical value value: Any - Any ⋮---- normalized = {} ⋮---- def canonical json bytes value: Mapping str, Any - bytes ⋮---- """Serialize an identity in the one form used for its SHA-256 key.""" ⋮---- @dataclass frozen=True class ExtractionCacheIdentityV3 ⋮---- requested urls: tuple str, ... attempt budget: Mapping str, Any effective context limits: Mapping str, Any output format: str include images: bool include raw html: bool render js: bool semantic spans: Mapping str, Any provider selection: Mapping str, Any provider endpoint config: Mapping str, Any url policy: Mapping str, Any storage policy: Mapping str, Any identity version: int = EXTRA… Evidence: `web_search_plus_mcp/cache_identity_v3.py`
- **Cache V3** (source_file): CACHE SCHEMA VERSION = 3 CACHE OWNER = "web-search-plus:v3" NORMALIZER VERSION = "runtime-v3-amendment-002" EXTRACTION IDENTITY VARY KEY = "extraction cache identity" ⋮---- @dataclass frozen=True class CacheLookupV3 ⋮---- disposition: str payload: Optional Dict str, Any = None entry id: Optional str = None age seconds: Optional int = None source contract version: Optional str = None legacy payload: Optional Dict str, Any = None ⋮---- attempts = payload.get "provider attempts" or successful = next routing = payload.get "routing receipt" or {} capability = str payload.get "capability" or "" extract legacy = legacy payload or {} if capability == "extract" else {} legacy provider = legacy paylo… Evidence: `web_search_plus_mcp/cache_v3.py`
- **Compat V3** (source_file): capability = Capability capability provider = str payload.get "provider" or "auto" default fallback = capability is Capability.EXTRACT or provider == "auto" routing = { cache = { client = {"accept contract versions": "3.0", "2.x" } ⋮---- query = unicodedata.normalize "NFC", str payload.get "query" or "" .strip mode = str payload.get "mode", "normal" ⋮---- options: Dict str, Any = { ⋮---- value = payload.get key ⋮---- locale = { ⋮---- urls = payload.get "urls" or ⋮---- urls = urls extract options: Dict str, Any = { ⋮---- spans query = payload.get "spans query" ⋮---- spans query = payload.get "query" ⋮---- def project execution: ExecutedV3, capability: Capability - Dict str, Any ⋮---- def v3… Evidence: `web_search_plus_mcp/compat_v3.py`
- **Contract V3** (source_file): CONTRACT VERSION = "3.0" ⋮---- class StrEnum str, Enum ⋮---- def str self - str ⋮---- class Capability StrEnum ⋮---- SEARCH = "search" EXTRACT = "extract" ⋮---- class ResponseStatus StrEnum ⋮---- OK = "ok" DEGRADED = "degraded" FAILED = "failed" ⋮---- class DegradedReason StrEnum ⋮---- SERVED STALE = "wsp.cache.served stale" CONTENT TRUNCATED = "wsp.content.truncated" URLS OMITTED = "wsp.extract.urls omitted" PARTIAL EXTRACTION = "wsp.extract.partial" BUDGET LIMITED = "wsp.budget.limited" FINGERPRINTING REDUCED = "wsp.independence.method degraded" ⋮---- class ErrorClass StrEnum ⋮---- INVALID REQUEST = "invalid request" UNSUPPORTED = "unsupported" CONFIG = "config" AUTH = "auth" QUOTA = "quo… Evidence: `web_search_plus_mcp/contract_v3.py`
- **Orchestrator V3** (source_file): PIPELINE STAGES: Tuple str, ... = ⋮---- @dataclass frozen=True class ProviderPlan ⋮---- candidate order: Tuple str, ... selected provider: str routing metadata: Dict str, Any = field default factory=dict mode: str = "classic" execution id: str = field ⋮---- def post init self - None ⋮---- PlanFn = Callable RequestV3, Dict str, Any , ProviderPlan ⋮---- @dataclass frozen=True class CapabilityExecution ⋮---- payload: Dict str, Any provider attempts: Tuple Any, ... = stages: Tuple str, ... = "provider attempt", ⋮---- unknown = set self.stages - set PIPELINE STAGES ⋮---- positions = PIPELINE STAGES.index stage for stage in self.stages ⋮---- ExecuteFn = Callable NormalizeFn = Callable RequestV3,… Evidence: `web_search_plus_mcp/orchestrator_v3.py`
- **Provider Adapter Protocol** (source_file): SEARCH ADAPTER PARAMETERS = EXTRACT ADAPTER PARAMETERS = ⋮---- @runtime checkable class SearchAdapter Protocol ⋮---- @runtime checkable class ExtractAdapter Protocol ⋮---- def signature matches adapter: Any, expected: tuple str, ... - bool ⋮---- parameters = tuple inspect.signature adapter .parameters.values ⋮---- errors: list str = contracts = ⋮---- actual providers = set dispatch ⋮---- """Fail module initialization when registry and adapters drift apart.""" ⋮---- errors = dispatch conformance errors ⋮---- def contract failure code: str - ProviderContractFailure ⋮---- """Validate a source-only provider envelope before orchestration consumes it. Errors contain stable codes only. Provider pa… Evidence: `web_search_plus_mcp/provider_adapter_protocol.py`
- **Provider Dispatch** (source_file): def resolve namespace: Any, name: str - Callable ..., Dict str, Any ⋮---- def locale prov: str, args: Any, config: Dict str, Any ⋮---- def call serper search search module, prov, args, key, config, routing info ⋮---- def call serpbase search search module, prov, args, key, config, routing info ⋮---- serpbase config = config.get "serpbase", {} ⋮---- def call brave search search module, prov, args, key, config, routing info ⋮---- brave config = config.get "brave", {} ⋮---- def call tavily search search module, prov, args, key, config, routing info ⋮---- def call linkup search search module, prov, args, key, config, routing info ⋮---- linkup config = config.get "linkup", {} ⋮---- def call quer… Evidence: `web_search_plus_mcp/provider_dispatch.py`
- **Provider Health** (source_file): PROVIDER HEALTH FILE = CACHE DIR / "provider health.json" COOLDOWN STEPS SECONDS = 60, 300, 1500, 3600 RETRY BACKOFF SECONDS = 1, 3, 9 ⋮---- RETRY JITTER FRACTION = 0.5 ⋮---- FAILURE DECAY SECONDS = 1800 ⋮---- RATE LIMIT MAX ATTEMPTS = 2 ⋮---- MAX RETRY AFTER WAIT SECONDS = 30.0 ⋮---- HEALTH LOCK = threading.Lock ⋮---- def retry delay attempt: int - float ⋮---- base = RETRY BACKOFF SECONDS min attempt, len RETRY BACKOFF SECONDS - 1 ⋮---- def ensure parent path: Path - None ⋮---- def load provider health - Dict str, Any ⋮---- data = json.load f ⋮---- def save provider health state: Dict str, Any - None ⋮---- def provider in cooldown provider: str - Tuple bool, int ⋮---- state = load provider… Evidence: `web_search_plus_mcp/provider_health.py`
- **Provider Registry** (source_file): PROVIDERS DIRECTORY = Path file .resolve .with name "providers.d" NON PRODUCTION DISCOVERY ENV = "WSP SDK ALLOW NON PRODUCTION" ⋮---- def non production discovery allowed - bool ⋮---- value = os.environ.get NON PRODUCTION DISCOVERY ENV, "" ⋮---- PROVIDER ID PATTERN = re.compile r"^ a-z0-9 + ?:- a-z0-9 + $" ENV VAR PATTERN = re.compile r"^ A-Z A-Z0-9 $" SEARCH PARAMETERS = EXTRACT PARAMETERS = ⋮---- BUILTIN PROVIDER SPECS = ⋮---- BUILTIN EXTRACT PROVIDER IDS = BUILTIN DEFAULT PROVIDER PRIORITY = provider specs = list BUILTIN PROVIDER SPECS discovered provider ids: set str = set ⋮---- PROVIDER SPECS: Dict str, ProviderSpec = {} SEARCH PROVIDER IDS: tuple str, ... = EXTRACT PROVIDER IDS: tuple… Evidence: `web_search_plus_mcp/provider_registry.py`
- **Provider Stats** (source_file): PROVIDER STATS FILE = CACHE DIR / "provider stats.json" ⋮---- MAX SAMPLES PER PROVIDER = 50 ⋮---- SAMPLE MAX AGE SECONDS = 7 24 3600 ⋮---- MIN SAMPLES FOR ADJUSTMENT = 5 ⋮---- MAX SCORE ADJUSTMENT = 1.0 ⋮---- LATENCY CEILING SECONDS = 8.0 ⋮---- PERFORMANCE BASELINE = 0.75 ⋮---- STATS LOCK = threading.Lock ⋮---- def load stats - Dict str, Any ⋮---- data = json.load f ⋮---- def save stats state: Dict str, Any - None ⋮---- sample = { ⋮---- state = load stats samples = state.get provider ⋮---- samples = ⋮---- def fresh samples samples: Any, now: float - List Dict str, Any ⋮---- cutoff = now - SAMPLE MAX AGE SECONDS ⋮---- def get provider performance provider: str, now: Optional float = None - O… Evidence: `web_search_plus_mcp/provider_stats.py`
- **Parse results** (source_file): BATCH TIMEOUT GRACE SECONDS = 5 ⋮---- FRESHNESS VALUES = "day", "week", "month", "year" ⋮---- PROVIDER FRESHNESS FORMATS: Dict str, Dict str, str = { ⋮---- def normalize freshness value: Optional str - Optional str ⋮---- normalized = str value .strip .lower ⋮---- def provider supports freshness provider: str - bool ⋮---- def map freshness for provider provider: str, freshness: Optional str - Optional str ⋮---- def freshness metadata provider: str, requested: str - Dict str, Any ⋮---- native = map freshness for provider provider, requested ⋮---- SEARCH TYPE VALUES = "search", "news" ⋮---- PROVIDER SEARCH TYPES: Dict str, Dict str, str = { ⋮---- def normalize search type value: Optional str -… Evidence: `web_search_plus_mcp/providers.py`
- **Request Gate V3** (source_file): SOURCE ONLY SEMANTICS = frozenset {"source results", "source text"} BANNED BODY KEYS = frozenset BANNED INSTRUCTION FRAGMENTS = ⋮---- def validate provider mode provider: str, capability: str - str ⋮---- spec = PROVIDER SPECS.get provider ⋮---- supported = spec.supports search semantics = spec.search output semantics ⋮---- supported = spec.supports extract semantics = spec.extract output semantics ⋮---- def walk value: Any ⋮---- def validate outbound body provider: str, body: Mapping str, Any - None ⋮---- lowered = key.lower ⋮---- text = value.lower Evidence: `web_search_plus_mcp/request_gate_v3.py`
- **Runtime V3** (source_file): def canonical url value: str - str ⋮---- parsed = urlsplit value host = parsed.hostname or "" .lower port = parsed.port netloc = host ⋮---- netloc = f"{host}:{port}" path = parsed.path or "/" ⋮---- def stable id prefix: str, parts: object - str ⋮---- raw = "\x1f".join str part for part in parts ⋮---- def valid rfc3339 value: object - str None ⋮---- parsed = datetime.fromisoformat value.replace "Z", "+00:00" ⋮---- def error message: str, provider: str None = None - ErrorV3 ⋮---- lowered = message.lower ⋮---- error class = ErrorClass.CONFIG code = "wsp.config.self hosted profile unavailable" ⋮---- code = "wsp.config.missing credentials" ⋮---- error class = ErrorClass.TIMEOUT code = "wsp.provi… Evidence: `web_search_plus_mcp/runtime_v3.py`
- **Server** (source_file): version = "1.1.0" ⋮---- SEARCH SCRIPT = Path file .parent / "search.py" app = Server "web-search-plus", version= version ⋮---- SEARCH PROVIDERS = { EXTRACT PROVIDERS = list EXTRACT PROVIDER IDS PRESETS = { ⋮---- CONFIG ENV VAR = "WEB SEARCH PLUS CONFIG" PROVIDER ALIASES = {"kilo perplexity": "kilo-perplexity"} RETIRED ANSWER PROVIDERS = {"perplexity", "kilo-perplexity"} ROUTING PROVIDER ORDER = list DEFAULT PROVIDER PRIORITY DEFAULT SEARCH SUBPROCESS TIMEOUT SECONDS = 75 RESEARCH SUBPROCESS GRACE SECONDS = 10 ⋮---- def canonical provider provider: str - str ⋮---- value = provider or "" .strip .lower ⋮---- def research subprocess timeout value: Any - int ⋮---- """Keep the outer process alive… Evidence: `web_search_plus_mcp/server.py`
- **State Migration V3** (source_file): BACKUP OWNER = "web-search-plus-state-migration-v3" BACKUP SCHEMA VERSION = 1 MIGRATION ID = "legacy-json-v1" MAX SOURCE BYTES = 8 1024 1024 MAX PROVIDERS = 256 MAX SAMPLES PER PROVIDER = 1000 PROVIDER RE = re.compile r"^ a-z0-9 a-z0-9 - {0,63}$" BACKUP ID RE = re.compile r"^ 0-9 {8}T 0-9 {6}Z- a-f0-9 {12} ?:- 0-9 + ?$" ⋮---- @dataclass frozen=True class MigrationReport ⋮---- action: str status: str dry run: bool sqlite available: bool health providers: int = 0 adaptive providers: int = 0 adaptive samples: int = 0 source digest: str None = None backup id: str None = None error code: str None = None ⋮---- def to dict self - dict str, Any ⋮---- def render migration report report: MigrationRep… Evidence: `web_search_plus_mcp/state_migration_v3.py`
- **State Store V3** (source_file): SCHEMA VERSION = 3 SHADOW EVALUATION RETENTION SECONDS = 30 24 60 60 SHADOW EVALUATION MAX ROWS = 10 000 DEFAULT OPEN SECONDS = { ⋮---- material = secret if secret else " " ⋮---- @dataclass frozen=True class CircuitKey ⋮---- provider: str capability: Capability endpoint: str credential fingerprint: str ⋮---- def values self - tuple str, str, str, str ⋮---- @dataclass frozen=True class CircuitRecord ⋮---- state: CircuitState = CircuitState.CLOSED failure count: int = 0 open until: Optional int = None updated at: int = 0 ⋮---- @dataclass frozen=True class AdmissionDecision ⋮---- allowed: bool circuit state: CircuitState skip reason: Optional SkipReason = None store available: bool = True bloc… Evidence: `web_search_plus_mcp/state_store_v3.py`
- **Init** (source_file): all = Evidence: `wsp_sdk/__init__.py`
- **Glama** (structured_config): { "$schema": "https://glama.ai/mcp/schemas/server.json", "maintainers": "robbyczgw-cla" , "name": "web-search-plus-mcp", "description": "Source-only multi-provider web search and bounded extraction with registry-backed routing, quality reports, and operator onboarding.", "repository": "https://github.com/robbyczgw-cla/web-search-plus-mcp", "homepage": "https://github.com/robbyczgw-cla/web-search-plus-mcp", "license": "MIT", "runtime": "python", "transport": "stdio" , "install": { "uvx": "uvx web-search-plus-mcp", "pip": "pip install web-search-plus-mcp" }, "mcp": { "command": "uvx", "args": "web-search-plus-mcp" }, "tools": { "name": "web search", "description": "Source-only web search thro… Evidence: `glama.json`
- **Request.Schema** (structured_config): { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://websearchplus.xyz/schema/v3/request.schema.json", "title": "RequestV3", "type": "object", "additionalProperties": false, "required": "contract version", "capability", "input" , "properties": { "contract version": { "const": "3.0" }, "request id": { "type": "string", "minLength": 1 }, "capability": { "$ref": " /$defs/Capability" }, "input": { "type": "object" }, "options": { "type": "object" }, "cache": { "$ref": " /$defs/CacheRequest" }, "routing": { "$ref": " /$defs/RoutingRequest" }, "budget": { "$ref": " /$defs/BudgetRequest" }, "client": { "$ref": " /$defs/ClientNegotiation" } }, "allOf": { "if": { "properties"… Evidence: `schemas/v3/request.schema.json`
- **Response.Schema** (structured_config): { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://websearchplus.xyz/schema/v3/response.schema.json", "title": "ResponseV3", "type": "object", "additionalProperties": false, "required": "contract version", "request id", "execution id", "capability", "status", "results", "observations", "policy actions", "source diversity", "provider attempts", "routing receipt", "cache status", "limits applied", "dedup clusters", "warnings" , "properties": { "contract version": { "const": "3.0" }, "request id": { "type": "string", "minLength": 1 }, "execution id": { "type": "string", "minLength": 1 }, "capability": { "$ref": " /$defs/Capability" }, "status": { "$ref": " /$defs/Resp… Evidence: `schemas/v3/response.schema.json`
- **Python** (source_file): Python pycache / .py cod $py.class .so .egg .egg-info/ dist/ build/ eggs/ parts/ var/ sdist/ develop-eggs/ .installed.cfg lib/ lib64/ wheels/ share/python-wheels/ .whl Evidence: `.gitignore`
- **Environment variables optional, for tool inspection** (source_file): LABEL org.opencontainers.image.title="web-search-plus-mcp" LABEL org.opencontainers.image.description="Multi-provider web search MCP server" LABEL org.opencontainers.image.source="https://github.com/robbyczgw-cla/web-search-plus-mcp" Evidence: `Dockerfile`
- **Gen Contract V3 Schemas** (source_file): ROOT = Path file .resolve .parents 1 ⋮---- OUT = ROOT / "schemas" / "v3" ⋮---- def enum schema enum cls ⋮---- def obj properties, required= , , additional=False ⋮---- value = { ⋮---- error = obj ⋮---- attempt = obj ⋮---- provider try error = obj provider try = obj ⋮---- request defs = { ⋮---- request schema = { ⋮---- response defs = { ⋮---- RECEIPT COMPLETION FIELDS = ⋮---- response schema = { ⋮---- parser = argparse.ArgumentParser ⋮---- args = parser.parse args ⋮---- stale = ⋮---- path = OUT / name rendered = json.dumps schema, indent=2, ensure ascii=False + "\n" Evidence: `scripts/gen_contract_v3_schemas.py`
- **Attempt Engine V3** (source_file): @dataclass frozen=True class AttemptContext ⋮---- provider: str capability: Capability endpoint: str credential fingerprint: str budget scope: str budget window: str budget units: int = 1 budget limit units: int = 3 daily budget scope: str None = None daily budget window: str None = None daily budget limit units: int None = None deadline monotonic: float None = None ⋮---- def post init self - None ⋮---- daily values = ⋮---- @property def circuit key self - CircuitKey ⋮---- @dataclass frozen=True class AttemptExecution ⋮---- payload: Optional Dict receipt: ProviderAttemptV3 ⋮---- class AttemptEngine ⋮---- @staticmethod def attempt id context: AttemptContext, started: int - str ⋮---- @staticm… Evidence: `web_search_plus_mcp/attempt_engine_v3.py`
- **Check config.json first** (source_file): CONFIG ENV VAR = "WEB SEARCH PLUS CONFIG" ⋮---- class SelfHostedProfileError ProviderConfigError ⋮---- error type = "self hosted profile unavailable" ⋮---- SUPPORTED PROFILES = frozenset {"standard", "self hosted"} SELF HOSTED SEARCH PROVIDER IDS = "searxng", KEYLESS PROVIDER IDS SELF HOSTED EXTRACT PROVIDER IDS = tuple KEYLESS EXTRACT PROVIDER IDS ⋮---- def is placeholder env value value: str - bool ⋮---- def clean env value value: str - Optional str ⋮---- def load env file ⋮---- DEFAULT CONFIG = { ⋮---- def deepcopy default config - Dict str, Any ⋮---- ROUTING PROVIDER NAMES = set PROVIDER SPECS VALID PROVIDERS = ROUTING PROVIDER NAMES ⋮---- def normalize routing provider config provider:… Evidence: `web_search_plus_mcp/config.py`
- **Daemon Tasks** (source_file): class DaemonTask ⋮---- def init self, fn: Callable ..., Any , args: Any, kwargs: Any ⋮---- def run self, fn: Callable ..., Any , args: tuple, kwargs: dict - None ⋮---- def done self - bool ⋮---- def result self, timeout: Optional float = None - Any Evidence: `web_search_plus_mcp/daemon_tasks.py`
- **Diversity V3** (source_file): MULTI LABEL SUFFIXES = frozenset ⋮---- TRACKING PARAMETER NAMES = frozenset ⋮---- DOMAIN DIVERSITY WEIGHT = 0.40 URL DUPLICATION WEIGHT = 0.30 CONTENT DIVERSITY WEIGHT = 0.20 PROVIDER MIX WEIGHT = 0.10 ⋮---- DEFAULT NEAR DUPLICATE THRESHOLD = 0.60 ⋮---- class DiversityComponents TypedDict ⋮---- domain diversity: float url duplication: float content diversity: float provider mix: float ⋮---- class DuplicateCandidate TypedDict ⋮---- kind: str kept: int dropped candidate: int ⋮---- class DominantDomain TypedDict ⋮---- domain: str share: float ⋮---- class DiversityReport TypedDict ⋮---- score: float components: DiversityComponents duplicates: List DuplicateCandidate dominant domain: Optional Do… Evidence: `web_search_plus_mcp/diversity_v3.py`
- **Env Loader** (source_file): def is placeholder env value value: str - bool ⋮---- stripped = value or "" .strip .strip '"' .strip "'" ⋮---- def clean env value value: str - Optional str ⋮---- """Return a real env value, or None for empty/template placeholders.""" ⋮---- TRUTHY VALUES = {"1", "true", "yes", "on"} ⋮---- def is truthy value: object - bool ⋮---- def get hermes env path - Path ⋮---- def candidate env paths anchor file: Union str, Path - List Path ⋮---- plugin dir = Path anchor file .resolve .parent paths = deduped: List Path = seen: Set str = set ⋮---- key = str path.expanduser ⋮---- def load env files anchor file: Union str, Path , environ: Optional MutableMapping str, str = None - List Path ⋮---- target en… Evidence: `web_search_plus_mcp/env_loader.py`
- **Errors V3** (source_file): MESSAGES = { ⋮---- CODES = { ⋮---- def classify provider error error: BaseException, , provider: str - ErrorV3 ⋮---- status = getattr error, "status code", None retry after = getattr error, "retry after", None class name = type error . name ⋮---- error class = ErrorClass.CONFIG ⋮---- error class = ErrorClass.TIMEOUT ⋮---- error class = ErrorClass.AUTH ⋮---- error class = ErrorClass.QUOTA ⋮---- error class = ErrorClass.RATE LIMIT ⋮---- error class = ErrorClass.TRANSIENT ⋮---- error class = ErrorClass.PROVIDER CONTRACT ⋮---- error class = ErrorClass.INTERNAL ⋮---- retryable = error class in { Evidence: `web_search_plus_mcp/errors_v3.py`
- **Extract** (source_file): EXTRACT PROVIDER PRIORITY = list EXTRACT PROVIDER IDS ⋮---- def daily preflight budget config: Dict str, Any - Dict str, Any ⋮---- raw off = os.environ.get "WSP BUDGET PREFLIGHT OFF" ⋮---- section = config.get "budget preflight" or {} limit = section.get "max daily provider calls" if isinstance section, dict else None ⋮---- def resolve extract provider priority config: Optional Dict str, Any = None - List str ⋮---- auto config = config.get "auto routing", {} if isinstance config, dict else {} ⋮---- auto config = {} raw priority = auto config.get "extract provider priority" ⋮---- raw values = raw priority.split "," ⋮---- raw values = raw priority ⋮---- raw values = ⋮---- providers: List str… Evidence: `web_search_plus_mcp/extract.py`
- **Ensure User-Agent is set required by some APIs like Exa/Cloudflare** (source_file): TRANSIENT HTTP CODES = {429, 503} ⋮---- version = "1.1.0" ⋮---- DEFAULT USER AGENT = f"ClawdBot-WebSearchPlus-MCP/{ version }" ⋮---- class ProviderRequestError Exception ⋮---- """Structured provider error with retry/cooldown metadata.""" ⋮---- def response header response, name: str - str ⋮---- """Return an HTTP response header from urllib response/error objects.""" ⋮---- value = response.getheader name ⋮---- headers = getattr response, "headers", None ⋮---- value = headers.get name ⋮---- value = None ⋮---- def read response body response - bytes ⋮---- """Read an urllib response body and decode supported Content-Encoding values.""" raw = response.read encoding = response header response, "C… Evidence: `web_search_plus_mcp/http_client.py`
- **Independence V3** (source_file): TRACKING KEYS = {"fbclid", "gclid", "dclid", "msclkid"} TOKEN RE = re.compile r" \w +", re.UNICODE MINHASH PERMUTATIONS = 32 MINHASH THRESHOLD = 0.65 ⋮---- def canonicalize url value: str - str ⋮---- parsed = urlsplit value scheme = parsed.scheme.lower host = parsed.hostname or "" .lower port = parsed.port netloc = host ⋮---- netloc = f"{host}:{port}" path = parsed.path or "/" ⋮---- path = path.rstrip "/" or "/" query items = query = urlencode sorted query items ⋮---- def tokens result: Dict str, Any - List str ⋮---- text = " ".join normalized = unicodedata.normalize "NFKC", text .casefold ⋮---- def shingles result: Dict str, Any - set str ⋮---- tokens = tokens result ⋮---- def minhash shin… Evidence: `web_search_plus_mcp/independence_v3.py`
- **Use last meaningful path segment as context** (source_file): ROUTING POLICY = "routing-v2" ⋮---- def title from url url: str - str ⋮---- parsed = urlparse url domain = parsed.netloc.replace "www.", "" Use last meaningful path segment as context segments = s for s in parsed.path.strip "/" .split "/" if s ⋮---- last = segments -1 .replace "-", " " .replace " ", " " ⋮---- last = re.sub r'\.\w{2,4}$', '', last ⋮---- def normalize result url url: str - str ⋮---- parsed = urlparse url.strip netloc = parsed.netloc or "" .lower ⋮---- netloc = netloc 4: path = parsed.path.rstrip "/" ⋮---- def deduplicate results across providers results by provider: List Tuple str, Dict str, Any , max results: int - Tuple List Dict str, Any , int ⋮---- deduped = seen = set de… Evidence: `web_search_plus_mcp/quality.py`
- **Research** (source_file): RESULT GRACE SECONDS = 0.25 ⋮---- provider errors: List Dict str, Any = now = now fn or time.monotonic start = now ⋮---- def remaining budget - Optional float ⋮---- pending: List Tuple int, str = tasks: Dict int, DaemonTask = {} workers = max workers or max 1, len research providers gate = threading.Semaphore workers ⋮---- def run gated provider name: str - Dict str, Any ⋮---- remaining = remaining budget ⋮---- results by index: Dict int, Tuple str, Dict str, Any = {} ⋮---- timeout = RESULT GRACE SECONDS ⋮---- timeout = remaining ⋮---- provider results: List Tuple str, Dict str, Any = ⋮---- diversity duplicates = ⋮---- merged candidates: List Dict str, Any = ⋮---- candidate = item.copy ⋮---… Evidence: `web_search_plus_mcp/research.py`
- **Conservative class-aware provider boosts positive and penalties negative** (source_file): def provider configured provider: str, config: Dict str, Any None = None - bool ⋮---- ROUTING POLICY = "routing-v2" ⋮---- def iter all selectable provider modes - Tuple str, ... ⋮---- ROUTING CLASS RULES: List Tuple str, Tuple str, ... = ⋮---- MULTILINGUAL ROUTING CLASS = "multilingual current" DEFAULT ROUTING CLASS = "general" ⋮---- LANGUAGE HINT PROVIDER BOOSTS: Dict str, List Tuple str, float = { ⋮---- LANGUAGE INFERENCE MIN MATCHES = 2 ⋮---- LANGUAGE INFERENCE STOPWORDS: Dict str, frozenset = { ⋮---- LANGUAGE INFERENCE CHAR HINTS: Dict str, str = { ⋮---- def infer query language query: str - Optional str ⋮---- lowered = query.lower words = set re.findall r"\w+", lowered counts: Dict str… Evidence: `web_search_plus_mcp/routing.py`
- **Determine provider** (source_file): http make get request = make get request http make request = make request ⋮---- def make get request args, kwargs ⋮---- def make request args, kwargs ⋮---- get cached result = cache get cache search result = cache put clear cache = cache clear get cache stats = cache stats ⋮---- def load env file ⋮---- ROUTING POLICY = "routing-v2" ⋮---- COMPATIBILITY SHIM DEPRECATION = { ⋮---- def get compatibility shim policy - Dict str, Any ⋮---- def sync routing dependencies - None ⋮---- class QueryAnalyzer routing.QueryAnalyzer ⋮---- def init self, args, kwargs ⋮---- def auto route provider args, kwargs ⋮---- def explain routing args, kwargs ⋮---- def provider auto allowed args, kwargs ⋮---- def sync p… Evidence: `web_search_plus_mcp/search.py`
- **Search Locale** (source_file): LANGUAGE INFERENCE MIN MATCHES = 2 LANGUAGE INFERENCE STOPWORDS: Dict str, frozenset str = { LANGUAGE INFERENCE CHAR HINTS: Dict str, str = { ⋮---- def infer query language query: str - Optional str ⋮---- lowered = query.lower words = set re.findall r"\w+", lowered counts: Dict str, int = {} ⋮---- count = len words & stopwords ⋮---- ranked = sorted counts.items , key=lambda item: -item 1 , item 0 ⋮---- FALLBACK COUNTRY = "us" FALLBACK LANGUAGE = "en" ⋮---- AUTO LANGUAGE = "auto" ⋮---- PROVIDER LOCALE CONFIG KEYS: Dict str, Tuple Optional str , Optional str = { ⋮---- LOCATION COUNTRY HINTS: Dict str, str = { ⋮---- LOCATION HINT PATTERNS: Tuple Tuple Any, str , ... = tuple ⋮---- def provider… Evidence: `web_search_plus_mcp/search_locale.py`
- **Shadow Policy V3** (source_file): POLICY ID = "shadow-quality" POLICY REVISION = "3.1" ⋮---- query = request.input "query" analysis = QueryAnalyzer dict config .analyze query scores = analysis "provider scores" candidates = tuple dict.fromkeys plan.candidate order ranked = shadow provider = Evidence: `web_search_plus_mcp/shadow_policy_v3.py`
- **Span Extraction V3** (source_file): Span = Dict str, Union int, float, str Ranker = Callable str, str , float ⋮---- TOKEN RE = re.compile r" ^\W + ?: '\N{RIGHT SINGLE QUOTATION MARK} ^\W + ?", re.UNICODE PARAGRAPH BREAK RE = re.compile r" ?:\r?\n \t \f\v {2,}" SENTENCE END RE = re.compile r" ? str ⋮---- """Return the canonical NFC string used for all span offsets.""" ⋮---- def tokens text: str - List str ⋮---- def trimmed candidate text: str, start: int, end: int - Candidate None ⋮---- def split long segment text: str, start: int, end: int, limit: int - List Candidate ⋮---- pieces: List Candidate = cursor = start ⋮---- boundary = min end, cursor + limit ⋮---- whitespace = text.rfind " ", cursor + max 1, limit // 2 , boundary… Evidence: `web_search_plus_mcp/span_extraction_v3.py`
- **Api** (source_file): SearchExecute = Callable Any, str, Any, str None, Mapping str, Any , Mapping str, Any , dict str, Any ExtractExecute = Callable ⋮---- @dataclass frozen=True, init=False class ProviderSpec ⋮---- provider: str env var: str display name: str description: str config section: str supports search: bool supports extract: bool capability labels: tuple str, ... auto allowed by default: bool recommended: bool free tier: str signup url: str upstream capabilities: tuple str, ... keyless: bool search output semantics: str None extract output semantics: str None provider fields allowlist: tuple str, ... rejected reason: str None execute search: SearchExecute None execute extract: ExtractExecute None supp… Evidence: `wsp_sdk/api.py`
- **Conformance** (source_file): def provider conformance errors - tuple str, ... ⋮---- errors = list dispatch conformance errors SEARCH DISPATCH, EXTRACT DISPATCH, PROVIDER SPECS ⋮---- payload = json.loads str exc ⋮---- def assert provider conformance - None ⋮---- errors = provider conformance errors Evidence: `wsp_sdk/conformance.py`
- **Errors** (source_file): class ProviderSDKError Exception ⋮---- class ProviderConfigError ProviderSDKError ⋮---- class ProviderContractFailure ProviderSDKError ⋮---- class ProviderRegistrationError ProviderSDKError ⋮---- class DuplicateProviderError ProviderRegistrationError ⋮---- class ProviderDiscoveryError ProviderSDKError ⋮---- class ProviderStartupDiagnostic ProviderDiscoveryError ⋮---- def init self, module: str, code: str - None Evidence: `wsp_sdk/errors.py`

## Rules the Host AI Must Follow

- **Treat this asset as pre-work context, not a runtime environment.**: The AI Context Pack contains only an evidence-backed understanding of the project, not the project's executable state. Evidence: `README.md`, `docs/MIGRATION_1_0.md`, `LICENSE`
- **When answering the user, distinguish what can be previewed from what can only be verified after install.**: The consumer value of the pre-install experience comes from reducing bad installs and misjudgments, not from pretending to be a real run. Evidence: `README.md`, `docs/MIGRATION_1_0.md`, `LICENSE`

## Questions the User Should Answer First

- Which host AI or local environment do you plan to use it in?
- Do you just want to experience the workflow first, or are you ready to actually install?
- What matters most to you: install cost, output quality, or conflicts with your existing rules?

## Acceptance Checks

- Every capability claim can be traced back to a file path in evidence_refs.
- AI_CONTEXT_PACK.md does not package previews as a real run.
- The user can understand who it fits, what it can do, how to start, and the risk boundaries within 3 minutes.

---

## Doramagic Context Augmentation

The following sections strengthen the repository context for a host AI. Human Manual data is a reading route, and pitfall notes become operating constraints.

## Human Manual Outline

Usage rule: this is only a reading route and salience signal, not factual authority. Concrete claims must still return to repo evidence or Claim Graph.

Host AI hard rules:
- Do not treat page titles, section order, summaries, or importance values as factual project evidence.
- When explaining the Human Manual outline, state that it is only a reading route or salience signal.
- Capability, installation, compatibility, runtime state, and risk claims must cite repo evidence, source paths, or Claim Graph.

- **Introduction and Quick Start**: importance `high`
  - source_paths: README.md, docs/MIGRATION_1_0.md, CHANGELOG.md, web_search_plus_mcp/__init__.py, web_search_plus_mcp/server.py
- **v3 Request/Response Contract and Orchestration Pipeline**: importance `high`
  - source_paths: web_search_plus_mcp/contract_v3.py, web_search_plus_mcp/orchestrator_v3.py, web_search_plus_mcp/runtime_v3.py, web_search_plus_mcp/request_gate_v3.py, web_search_plus_mcp/bounded_context_v3.py
- **Provider System, Routing, and Multi-Provider Fallback**: importance `high`
  - source_paths: web_search_plus_mcp/providers.py, web_search_plus_mcp/provider_registry.py, web_search_plus_mcp/provider_dispatch.py, web_search_plus_mcp/provider_adapter_protocol.py, web_search_plus_mcp/provider_health.py
- **Caching, Policy Layer, and Operator Workflows**: importance `high`
  - source_paths: web_search_plus_mcp/cache.py, web_search_plus_mcp/cache_v3.py, web_search_plus_mcp/cache_identity_v3.py, web_search_plus_mcp/state_store_v3.py, web_search_plus_mcp/state_migration_v3.py

## Repo Inspection Evidence

- repo_clone_verified: true
- repo_inspection_verified: true
- repo_commit: `f9fe6737baf5fe8cab3c5eb9746fa6ac6a765fd7`
- inspected_files: `Dockerfile`, `README.md`, `pyproject.toml`, `uv.lock`, `docs/MIGRATION_1_0.md`

Host AI hard rules:
- Without repo_clone_verified=true, do not claim that the source code has been read.
- Without repo_inspection_verified=true, do not write README, docs, or package-file conclusions as facts.
- Without quick_start_verified=true, do not claim that the Quick Start path has run successfully.

## Doramagic Pitfall Constraints

These rules come from Doramagic discovery, validation, or compilation findings. The host AI must treat them as operating constraints, not background notes.

### Constraint 1: Installation risk requires verification

- Trigger: Developers should check this installation risk before relying on the project: web-search-plus-mcp v0.11.0
- Host AI rule: Before packaging this project, run the relevant install/config/quickstart check for: web-search-plus-mcp v0.11.0. Context: Observed when using python
- Why it matters: Upgrade or migration may change expected behavior: web-search-plus-mcp v0.11.0
- Evidence: failure_mode_cluster:github_release | https://github.com/robbyczgw-cla/web-search-plus-mcp/releases/tag/v0.11.0
- Hard boundary: Do not present this pitfall as solved, verified, or ignorable unless later evidence explicitly closes it.

### Constraint 2: Installation risk requires verification

- Trigger: Developers should check this installation risk before relying on the project: web-search-plus-mcp v0.17.0
- Host AI rule: Before packaging this project, run the relevant install/config/quickstart check for: web-search-plus-mcp v0.17.0. Context: Observed when using python
- Why it matters: Upgrade or migration may change expected behavior: web-search-plus-mcp v0.17.0
- Evidence: failure_mode_cluster:github_release | https://github.com/robbyczgw-cla/web-search-plus-mcp/releases/tag/v0.17.0
- Hard boundary: Do not present this pitfall as solved, verified, or ignorable unless later evidence explicitly closes it.

### Constraint 3: Installation risk requires verification

- Trigger: Developers should check this installation risk before relying on the project: web-search-plus-mcp v1.0.0
- Host AI rule: Before packaging this project, run the relevant install/config/quickstart check for: web-search-plus-mcp v1.0.0. Context: Observed when using python
- Why it matters: Upgrade or migration may change expected behavior: web-search-plus-mcp v1.0.0
- Evidence: failure_mode_cluster:github_release | https://github.com/robbyczgw-cla/web-search-plus-mcp/releases/tag/v1.0.0
- Hard boundary: Do not present this pitfall as solved, verified, or ignorable unless later evidence explicitly closes it.

### Constraint 4: Installation risk requires verification

- Trigger: Developers should check this installation risk before relying on the project: web-search-plus-mcp v1.1.0
- Host AI rule: Before packaging this project, run the relevant install/config/quickstart check for: web-search-plus-mcp v1.1.0. Context: Observed when using python
- Why it matters: Upgrade or migration may change expected behavior: web-search-plus-mcp v1.1.0
- Evidence: failure_mode_cluster:github_release | https://github.com/robbyczgw-cla/web-search-plus-mcp/releases/tag/v1.1.0
- Hard boundary: Do not present this pitfall as solved, verified, or ignorable unless later evidence explicitly closes it.

### Constraint 5: Configuration risk requires verification

- Trigger: Developers should check this configuration risk before relying on the project: v0.12.0
- Host AI rule: Before packaging this project, run the relevant install/config/quickstart check for: v0.12.0. Context: Source discussion did not expose a precise runtime context.
- Why it matters: Upgrade or migration may change expected behavior: v0.12.0
- Evidence: failure_mode_cluster:github_release | https://github.com/robbyczgw-cla/web-search-plus-mcp/releases/tag/v0.12.0
- Hard boundary: Do not present this pitfall as solved, verified, or ignorable unless later evidence explicitly closes it.

### Constraint 6: Configuration risk requires verification

- Trigger: Developers should check this configuration risk before relying on the project: v1.0.0: auto routing builds single-candidate plans — no fallback on quota/rate-limit
- Host AI rule: Before packaging this project, run the relevant install/config/quickstart check for: v1.0.0: auto routing builds single-candidate plans — no fallback on quota/rate-limit. Context: Source discussion did not expose a precise runtime context.
- Why it matters: Developers may misconfigure credentials, environment, or host setup: v1.0.0: auto routing builds single-candidate plans — no fallback on quota/rate-limit
- Evidence: failure_mode_cluster:github_issue | https://github.com/robbyczgw-cla/web-search-plus-mcp/issues/27
- Hard boundary: Do not present this pitfall as solved, verified, or ignorable unless later evidence explicitly closes it.

### Constraint 7: Configuration risk requires verification

- Trigger: Developers should check this configuration risk before relying on the project: web-search-plus-mcp v0.13.0
- Host AI rule: Before packaging this project, run the relevant install/config/quickstart check for: web-search-plus-mcp v0.13.0. Context: Source discussion did not expose a precise runtime context.
- Why it matters: Upgrade or migration may change expected behavior: web-search-plus-mcp v0.13.0
- Evidence: failure_mode_cluster:github_release | https://github.com/robbyczgw-cla/web-search-plus-mcp/releases/tag/v0.13.0
- Hard boundary: Do not present this pitfall as solved, verified, or ignorable unless later evidence explicitly closes it.

### Constraint 8: Configuration risk requires verification

- Trigger: Developers should check this configuration risk before relying on the project: web-search-plus-mcp v0.14.0
- Host AI rule: Before packaging this project, run the relevant install/config/quickstart check for: web-search-plus-mcp v0.14.0. Context: Observed when using python
- Why it matters: Upgrade or migration may change expected behavior: web-search-plus-mcp v0.14.0
- Evidence: failure_mode_cluster:github_release | https://github.com/robbyczgw-cla/web-search-plus-mcp/releases/tag/v0.14.0
- Hard boundary: Do not present this pitfall as solved, verified, or ignorable unless later evidence explicitly closes it.
