Add searxng-search plugin: self-hosted SearXNG web search Skill - #1
Open
Fectivnfy112357 wants to merge 1 commit into
Open
Add searxng-search plugin: self-hosted SearXNG web search Skill#1Fectivnfy112357 wants to merge 1 commit into
Fectivnfy112357 wants to merge 1 commit into
Conversation
A zero-dependency Python Skill that lets the agent run search queries
against the user's own self-hosted SearXNG instance. Supports bearer /
basic auth, TOML or JSON config, and the standard SearXNG filters
(categories, engines, time range, language, safe search, pagination).
Solves: web search inside an agent usually means a vendor API with
credentials and quotas. Operators that already run SearXNG (a
privacy-respecting metasearch engine) get a first-class search tool
against their own instance — one config file, no vendor SDK, no
tracking.
Security model (matched to mcode-plugin-guide red lines):
- HTTPS by default. Plain HTTP is accepted only for loopback hosts
(127.0.0.1, ::1, localhost) or for non-loopback hosts when the
config contains an explicit `allow_insecure_http = true` opt-in,
which emits a strong warning.
- Auth values can be referenced via `$ENV_VAR` / `${ENV_VAR}` in the
config so the plaintext never lives in the file. On POSIX the
script warns when the file is readable beyond the owner.
- HTTP redirects are blocked entirely (custom `HTTPRedirectHandler`).
The default urllib handler would forward the `Authorization` header
to the redirect target, which can leak the token to a different
origin and silently downgrade HTTPS to HTTP. Point `base_url`
directly at the final endpoint, or front the instance with a
same-origin reverse proxy.
- 401 / 403 / 407 response bodies are not echoed to stderr because
they frequently reflect back the credentials the server saw.
Other 4xx / 5xx bodies run through a two-pass redactor: an
exact-value secret registry (per request) plus shape-based
fallbacks (Bearer / Basic / github_pat_ / gh[pousr]_ / token= /
common env-var names).
- Numeric config fields are type-validated at load time
(`timeout = "30"` etc. fail fast with a clear message instead of
surfacing a raw TypeError from urllib).
- Response body is capped at 10 MiB to prevent runaway reads on a
misbehaving or hijacked endpoint.
Network (two levels, both disclosed in README, SKILL.md, and
plugin.json):
- Direct: the script makes a single request to the user's
configured `base_url`. Nothing else.
- Downstream: the user's SearXNG instance then forwards the query,
language code, and selected categories to its own configured
upstream engines (Google, Bing, DuckDuckGo, Brave, Baidu, etc., as
enabled by the instance operator). The script does not see or
control that hop.
Tested:
- `py -3 skills/searxng-search/scripts/run_tests.py` → 77 pass,
2 POSIX-only skipped (expected on Windows).
- `node --test test/searxng-search.test.mjs` → 1 pass, 0 fail
(the Node bridge that the repo's `npm test` will discover).
- `node scripts/validate.mjs` → `OK plugin
Fectivnfy112357/searxng-search`.
Files (28):
- `plugin.json`, `README.md`, `LICENSE` (MIT)
- `skills/searxng-search/SKILL.md` and `.gitignore`
- `skills/searxng-search/references/configuration.md`
- `skills/searxng-search/scripts/search.py` (the runtime)
- `skills/searxng-search/scripts/run_tests.py` (unittest entry)
- `skills/searxng-search/scripts/tests/` (7 test modules + fixtures)
- `test/searxng-search.test.mjs` (Node bridge for the repo's
`node --test` discoverer)
No installer. No write operations outside reading the local config
file. No telemetry. No third-party services run by this script.
No symlinks. No native binaries. No credentials or private endpoints
are committed; the test suite is fully offline.
antianqi
added a commit
to antianqi/MiniMax-Code-Plugins-1
that referenced
this pull request
Aug 22, 2026
Fixes for review comments from hetaoBackend (commit fce7c5f): MiniMax-AI#1 detector hard-coded path: resolve the [userprofile]/.minimax-code directory at runtime via the mcode node process cmdline (regex on @minimax-ai/code/cli.js), with fallbacks to $env:USERPROFILE/.minimax-code, $env:APPDATA/minimax-code, and the current working directory. Override with -Root [path]. MiniMax-AI#2 idle fallback unreachable: mtime cache now returns the last inferred message instead of null, so the 60s stale -> idle branch fires every poll. Verified locally: idle :: already idle 195s after 65s of inactivity. #2b session log: prefer ledger.jsonl (mcode v2 event stream) and fall back to messages.jsonl when ledger is missing. Both formats are handled in Infer-State (kind/phase for ledger, message.role for messages). MiniMax-AI#3 PID reuse safety: start/stop-{island,detect-island}.ps1 now verify the target PID command line contains the expected script path before acting. Stale PIDs and PID-reused processes are refused with a REFUSED log line instead of being killed. MiniMax-AI#4 wrap-tool.ps1 shell-injection: removed Invoke-Expression entirely. The wrapper is now status-only; the agent runs the command via mcode's own bash tool and passes -ExitCode to publish the outcome. Documented in README + SKILL.md. MiniMax-AI#5 README: -Enable -> -Action Enable to match autostart.ps1 parameter set. MiniMax-AI#6 start-island.ps1 readiness: dropped the 'about to ShowDialog' log wait (which was never emitted). Now polls MainWindowHandle != 0 every 500ms for up to 8s. Tests: validator reports OK plugin antianqi/mcode-island. wrap-tool 6-state matrix verified locally (working / done / waiting / error).
antianqi
added a commit
to antianqi/MiniMax-Code-Plugins-1
that referenced
this pull request
Aug 22, 2026
The previous implementation had a "fast path" that did
`await import(lintScript).then(mod => mod.lint(skillPath))` in-process.
The default host linter at
`~/.minimax/.builtin-skills/skill-creator/scripts/lint-skill.js` calls
`process.exit(2)` when invoked without CLI arguments, and `process.exit`
is not catchable from JS — so a default invocation (no `run_lint=false`
override) terminated the entire MCP server before it could return a
JSON-RPC response.
- `lib/lint.js`: drop the in-process fast path; always run the
linter as a child process. Cost: one extra `node` spawn + a
staged `.mjs` in `os.tmpdir()` per `convert` call (~100 ms). The
trade is worth it: the MCP server is now guaranteed to survive a
misbehaving linter.
- `lib/lint.js`: pre-flight `fs.stat(lintScript)` so a missing host
linter surfaces as `{ ok: false, code: -1, stderr: 'lint script
not available: ...' }` instead of an uncaught ENOENT from
`fs.readFile` inside `stageMjsInTmp`.
- `tests/lint.test.mjs`: rewrite around the subprocess-only model.
Replace the fast-path test with three cases:
- subprocess path stages in `os.tmpdir()`, install dir untouched
- linter calls `process.exit(2)` and the MCP server still
returns `{ ok: false, code: 2 }`
- missing lintScript returns `{ ok: false, code: -1, stderr }`
`node --test plugins/antianqi/skill-bridge/tests/*.test.mjs` reports
54/54 pass (was 53/53; +1 new case for missing linter).
antianqi
added a commit
to antianqi/MiniMax-Code-Plugins-1
that referenced
this pull request
Aug 23, 2026
MiniMax-AI#2) The review pointed out four concrete API mismatches between the Skills and the SDK they call. We pulled the actual `acp_tools.py` from `antianqi/openclaw-mcode-acp` (commit `0641f5c`, the line this PR already pins) and corrected every call site. - **acp-task-dispatch/SKILL.md** (review MiniMax-AI#1): - `from acp_tools import create_task, get_task, list_history` → `history` (the function is named `history`, not `list_history`). - `task = create_task(...)` then `task["task_id"]` → `task_id = create_task(...)` (the function returns the `task_id` string directly, not a mapping). - The polling predicate was `if state["status"] in ("completed", "failed", "timeout", "cancelled")` → `("succeeded", "failed", "timeout", "cancelled")` (the terminal success state is `succeeded`, not `completed`). - `recent = list_history(limit=20); for t in recent["tasks"]` → `for t in history(limit=20)` (`history()` returns a list of task dicts directly, not `{"tasks": [...]}`). - **acp-collab/SKILL.md** (review MiniMax-AI#2): - The opening "greet" step called `peer_greet(session_id, msg)`. `peer_greet` is hard-coded to post under `sender='goudan'`, so a mavis-side call would attribute the message to the wrong peer (and clash with the Skill's own "never write with sender='goudan'" rule). Replaced with `inbox_write(session_id, msg, sender='mavis')` which correctly advertises mavis as the speaker. - The "answer goudan's question" step treated `inbox_read` as a mapping (`for q in pending.get("messages", [])`). `inbox_read` returns a **list** directly, not `{"messages": ...}`. Simplified the loop accordingly. - **README.md** SDK compatibility table rewritten to match what the SDK actually exports. Every row now shows the correct return type. Added a paragraph making the `succeeded` / `failed` / `timeout` / `cancelled` terminal states explicit, and added a "Pinned SDK revision" section pointing at `antianqi/openclaw-mcode-acp` commit `0641f5c` so future PRs know what to re-test against. `node scripts/validate.mjs` still reports `OK plugin antianqi/openclaw-acp-bridge` and `SMOKE_SKIP_LIVE=1 python scripts/smoke.py` reports 8/8 PASS.
antianqi
added a commit
to antianqi/MiniMax-Code-Plugins-1
that referenced
this pull request
Aug 23, 2026
MiniMax-AI#2) The review pointed out four concrete API mismatches between the Skills and the SDK they call. We pulled the actual `acp_tools.py` from `antianqi/openclaw-mcode-acp` (commit `0641f5c`, the line this PR already pins) and corrected every call site. - **acp-task-dispatch/SKILL.md** (review MiniMax-AI#1): - `from acp_tools import create_task, get_task, list_history` → `history` (the function is named `history`, not `list_history`). - `task = create_task(...)` then `task["task_id"]` → `task_id = create_task(...)` (the function returns the `task_id` string directly, not a mapping). - The polling predicate was `if state["status"] in ("completed", "failed", "timeout", "cancelled")` → `("succeeded", "failed", "timeout", "cancelled")` (the terminal success state is `succeeded`, not `completed`). - `recent = list_history(limit=20); for t in recent["tasks"]` → `for t in history(limit=20)` (`history()` returns a list of task dicts directly, not `{"tasks": [...]}`). - **acp-collab/SKILL.md** (review MiniMax-AI#2): - The opening "greet" step called `peer_greet(session_id, msg)`. `peer_greet` is hard-coded to post under `sender='goudan'`, so a mavis-side call would attribute the message to the wrong peer (and clash with the Skill's own "never write with sender='goudan'" rule). Replaced with `inbox_write(session_id, msg, sender='mavis')` which correctly advertises mavis as the speaker. - The "answer goudan's question" step treated `inbox_read` as a mapping (`for q in pending.get("messages", [])`). `inbox_read` returns a **list** directly, not `{"messages": ...}`. Simplified the loop accordingly. - **README.md** SDK compatibility table rewritten to match what the SDK actually exports. Every row now shows the correct return type. Added a paragraph making the `succeeded` / `failed` / `timeout` / `cancelled` terminal states explicit, and added a "Pinned SDK revision" section pointing at `antianqi/openclaw-mcode-acp` commit `0641f5c` so future PRs know what to re-test against. `node scripts/validate.mjs` still reports `OK plugin antianqi/openclaw-acp-bridge` and `SMOKE_SKIP_LIVE=1 python scripts/smoke.py` reports 8/8 PASS.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR adds
A new Skill plugin,
searxng-search, that lets the agent run a websearch query against the user's own self-hosted SearXNG instance via a
zero-dependency Python script. Operators that already self-host SearXNG
(a privacy-respecting metasearch engine) get a first-class search tool
without going through a vendor API or installing another binary.
Filters supported out of the box: categories, engines, time range,
language, safe search, pagination. Bearer and basic auth, with secrets
referenced as
$ENV_VARso plaintext never lives in the config file.The problem it solves
Web search inside an agent usually means a vendor API with credentials
and quotas. If you already run SearXNG, this skill gives the agent a
direct search tool against it — one config file, no vendor SDK, no
tracking.
Try it
After installing from
/plugins→ Local and configuring yourinstance (see
skills/searxng-search/references/configuration.md),ask the agent:
Expected result: a formatted list of results (title, URL, snippet)
from your SearXNG instance, filtered by the requested category / time
range / language.
Direct usage from the skill directory:
Dependencies and supported platforms
tomllib; legacy JSON config stillworks on older Python). Zero pip dependencies.
provide it; no public instance is bundled.
node --testbridgein
test/searxng-search.test.mjsuses the platform-appropriateinterpreter discovery order (
py -3→python→python3onWindows;
python3→pythonon POSIX) and validates each candidatewith
--versionbefore running, so it does not produce a falsepass on a host that has no usable Python.
Network and data behavior
This skill has two levels of network destinations; both matter.
configured
base_url. HTTPS by default; plain HTTP is allowed onlyfor loopback hosts (127.0.0.1, ::1, localhost) or for non-loopback
hosts with an explicit
allow_insecure_http = trueopt-in, whichemits a strong warning because Authorization headers and search
queries would travel in cleartext. The script never follows HTTP
redirects: a 30x response is an error, because following a redirect
could forward the Authorization header to a host the user did not
configure. Point
base_urldirectly at the final endpoint, orfront the instance with a same-origin reverse proxy.
the query, language code, and selected categories to
the upstream engines it has been configured with (Google, Bing,
DuckDuckGo, Brave, Baidu, etc., as enabled by the instance
operator). Those engines receive the request content from the
instance — the script does not see or control that hop. If query
contents reaching the upstream engines is a concern, configure
your instance to use engines you trust, or self-host engines
locally.
No telemetry. No third-party services run by this script.
Authorization headers and search queries are sent only to the
configured
base_url; the script never sends them anywhere else.Compliance declarations
or in the test fixtures. The test suite is fully offline (no
network, no
ghbinary, no real SearXNG instance).~/.dsh,~/.minimax,~/.pi, or any other user configuration directory.It only reads the local config file at
~/.config/agents/searxng.toml(or.json).only (
urllib,tomllib,json,argparse,base64).own config; no MCP server; no commands besides the single Skill
that calls
python3 scripts/search.py.redact_secretspipeline(exact-value registry + shape-based fallback). 401 / 403 / 407
response bodies are not echoed because they frequently reflect
back the credentials the server saw. Custom
headersvalues usethe same
$ENV_VARresolution and redaction registration asauth.*.a misbehaving or hijacked endpoint.
Test evidence
py -3 skills/searxng-search/scripts/run_tests.py→ 77 pass, 2skipped (POSIX-only, expected on Windows), 0 fail. The suite
covers:
reject; opt-in warning; non-
http(s)schemes reject)query and fragment from
Location:; opener installs the safehandler exactly once and no default redirect handler; integration
through
main()does not echo the Authorization header)token never leaks into the URL; custom
headersvalues followthe same env-var + redaction rules as
auth.*; User-Agentoverride; non-string header values rejected)
base_url, env-var resolution, numeric type validation,POSIX-mode warning with Windows skip)
short / non-shape tokens, substring-first ordering, reset,
empty-value no-op, idempotency, stderr redaction)
error,HTTPError body redacted, URL error, timeout, 401 / 403 / 407
body suppressed, JSON-array root, non-UTF-8, oversized
response, oversized error body)
node --test test/searxng-search.test.mjs→ 1 pass, 0 fail.The Node bridge spawns the first usable Python interpreter with
--versionvalidation and assertspython run_tests.pyexits 0,so the repo's
node --testdiscoverer picks this up undernpm test.node scripts/validate.mjs→OK plugin Fectivnfy112357/searxng-search. The package shape (manifestfields, SKILL.md frontmatter, LICENSE, README, no symlinks, no
BOM, no TODO residue) is clean.
Files (18, +2379 / -0)
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.