Skip to content

feat(search): accept an optional Parallel API key - #87

Open
gioknx wants to merge 1 commit into
1broseidon:mainfrom
gioknx:parallel-api-key
Open

gioknx wants to merge 1 commit into
1broseidon:mainfrom
gioknx:parallel-api-key

Conversation

@gioknx

@gioknx gioknx commented Oct 16, 2026

Copy link
Copy Markdown

Parallel's Search MCP (https://search.parallel.ai/mcp) is free without a key at a lower rate limit. Its docs say: "Pass a Parallel API key as a Bearer token (Authorization: Bearer <key>) to unlock higher limits." (https://docs.parallel.ai/integrations/mcp/search-mcp). The parallel backend is keyless and had nowhere to put one.

This follows the exa, keenable and youcom pattern: keyless by default, with an optional key that lifts the hosted caps.

What changes

  • The descriptor declares config.KeyPool("parallel_api_key", "parallel_api_keys"). ketch config set, ketch config discovery (parallel_api_key_set, parallel_api_keys_count), the KETCH_PARALLEL_API_KEY env override (comma-separated for several keys), doctor gating and auto promotion all come from the descriptor.
  • A configured key is sent as Authorization: Bearer <key>. A 401 or 429 retries once with another key from the pool. As with exa and keenable, a 403 is not retried and gets the generic status error.
  • Keyed 401, 402 and 429 responses return parallel: invalid API key (key 1 of 2; set via: ketch config set parallel_api_key <key>), parallel: payment required (...) and parallel: rate limited (...).
  • Every keyed search error passes through an httpx.Redactor built from the key pool, and the keyed doctor detail does too. A key the service echoes into a status body, a JSON-RPC error, or a redirect Location that net/http quotes is replaced with [redacted], and errors.Is still sees cancellation and timeouts. To build that redactor from plain values, httpx gets NewValueRedactor. NewRedactor now delegates to it, with no behaviour change.
  • ProbeParallel sends the key on the tools/list probe. Doctor reports a rejected key as misconfigured, and every key in the pool is checked through health.ProbeKeyPool.
  • README, MANUAL, the skill references and CHANGELOG (Unreleased) list parallel_api_key among the optional keys.

Without a key, nothing changes: the request is the same, the errors are the same (parallel returned status N: <body>), and the doctor probe is still health.ProbeMCP.

Testing

  • make build, make lint (golangci-lint 2.15.0) and make test pass. CGO_ENABLED=0 go build passes.
  • New httptest cases, no network:
    • The Authorization header is present with a key, and absent without one or with a blank one.
    • The descriptor builds the pool from parallel_api_key + parallel_api_keys.
    • Keyed 401/402/429 errors are covered. A keyed 403 makes one call and returns the generic error. Keyless 401/429/500 errors are byte-for-byte unchanged.
    • Adversarial servers echo the key in a 500 body, in a JSON-RPC error message, and in a redirect Location (a redirect loop, for search and for the doctor probe). The tests assert that no raw, query-escaped or path-escaped form of the key appears.
    • A cancelled keyed request whose error text contained the key is redacted and still matches context.DeadlineExceeded.
    • Probe outcomes, keyless and keyed.
    • Parallel 401 and 429 rows were added to TestBackendsRetryEveryCredentialStatus.
  • Fixtures regenerated with UPDATE_REGISTRY_GOLDENS=1 go test ./cmd/ ./doctor/. Only the two parallel_api_key* entries were added.
  • Manually: KETCH_PARALLEL_API_KEY=a,b ketch config --json reports parallel_api_key_set: true, parallel_api_keys_count: 2.
  • Live, with this branch built: no key returns results as before; an invalid key fails with parallel: invalid API key (key 1 of 1; set via: ketch config set parallel_api_key <key>) and ketch doctor reports it as misconfigured; a valid key returns results and doctor reports ok.

No affiliation with Parallel.

Parallel's Search MCP is free without a key at a lower rate limit, and
documents passing an API key as a Bearer token to unlock higher limits
(https://docs.parallel.ai/integrations/mcp/search-mcp). The backend had
no place to put one.

- parallel_api_key / parallel_api_keys declared with config.KeyPool, so
  config set, discovery, KETCH_PARALLEL_API_KEY, doctor gating and auto
  promotion follow the descriptor like exa, keenable and youcom
- a configured key is sent as Authorization: Bearer; a 401 or 429 retries
  once with another key from the pool, as exa and keenable do
- keyed 401, 402 and 429 return errors that name the key by position
- every keyed search error and doctor detail passes through an
  httpx.Redactor built from the pool (new httpx.NewValueRedactor), so a
  key echoed into a status body, a JSON-RPC error or a redirect Location
  is replaced with [redacted]; cancellation still classifies
- without a key, requests, errors and the doctor probe are unchanged

1broseidon commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Triage (automated): NOTIFY — needs human review.

What it is: Adds an optional API key (parallel_api_key / parallel_api_keys) to the keyless parallel search backend, following the exa/keenable/youcom pattern (keyless by default, key lifts rate limits). Includes key redaction via a new httpx.NewValueRedactor, retry-on-401/429, doctor probe gating, and regenerated config/doctor golden fixtures.

Why it needs review, not auto-fix: This is a new feature, not a bug fix, and it trips the config-schema / output-format gate explicitly called out in triage guardrails — it adds new config keys and new fields to ketch config --json (parallel_api_key_set, parallel_api_keys_count). It's also a 13-file, 463-line diff, well past "tight." Per CONTRIBUTING.md this is new-provider-capability territory that should go through normal maintainer review (admission criteria, API shape, redaction correctness) rather than automated triage.

Assessment: The PR description is thorough — new httptest coverage for header presence, keyed error codes, redaction against echoed keys in bodies/JSON-RPC errors/redirect Location, golden fixtures regenerated with only the expected two new entries, and live manual testing described. No obvious red flags spotted in a surface read of the diff. Recommend a normal review pass focused on: redaction correctness (adversarial key-echo cases), and whether delegating NewRedactor to NewValueRedactor changes behavior for existing keyed providers.


Automated daily triage.


Generated by Claude Code

@1broseidon 1broseidon added needs-review Triage loop flagged this for human approval triaged Processed by the daily triage loop labels Oct 17, 2026 — with Claude

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review Triage loop flagged this for human approval triaged Processed by the daily triage loop

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants