Skip to content

Add remote (Streamable HTTP + OAuth) server, search tool, and tests#13

Merged
gradill22 merged 6 commits into
mainfrom
feat/remote-server-search
Jul 23, 2026
Merged

Add remote (Streamable HTTP + OAuth) server, search tool, and tests#13
gradill22 merged 6 commits into
mainfrom
feat/remote-server-search

Conversation

@gradill22

@gradill22 gradill22 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Adds a hosted/remote transport alongside the existing stdio server, plus the search tool — and wires up a test step CI never had.

What's here

  • src/http.ts — a sibling to the stdio entry point speaking MCP over Streamable HTTP, so hosted clients (Claude.ai custom connectors) can connect without launching a local process. It calls the same createServer per request, so the tool surface is identical across transports. Dependency-free (raw Node http).
  • Stateless by design — access tokens expire hourly, so binding one to a long-lived session would break mid-session; per-request binding always uses the token just presented. Verified end-to-end that the MCP initialize handshake and a subsequent tools/list both work stateless with a real client.
  • OAuth resource-server role — serves /.well-known/oauth-protected-resource and answers unauthenticated/expired requests with 401 WWW-Authenticate, which is what makes a client run (or refresh) the flow. A token is validated by one probe to the API; an unreachable API fails closed with 503.
  • search tool, mirroring the API's /search.
  • test/tool-parity.test.mjs — zero-dependency (node:test) test asserting the stdio and HTTP entry points expose an identical tool list. CI now runs it; previously this repo had no test step at all.
  • railway.toml to deploy the remote server as its own service.

🚨 CI is red for a pre-existing packaging bug — please read before merging

CI here does not just need search(). Root-caused from the failing run:

  1. wellmarked@^1.1.1 is unsatisfiable. Only 1.1.0 is published on npm, so the range can never resolve. This has been broken since the dependency was switched from file:../JS-TS-SDK to the published range.
  2. package-lock.json is stale — it still pins node_modules/wellmarked to ../JS-TS-SDK (the old file: link). In an isolated CI checkout that path doesn't exist, so npm ci silently skipped ~29 packages (105 installed vs 134 locked), which also dropped @types/node — hence the 57 TS2591 Cannot find name 'process' errors on top of TS2307 Cannot find module 'wellmarked'.

Locally everything passes only because that stale symlink resolves to the sibling checkout.

Remediation (at release time, in this order)

  1. Merge + publish JS-TS-SDK#10 to npm as a version that contains search() (e.g. 1.2.0).
  2. In this repo: bump wellmarked to that version and run npm install to regenerate the lockfile off the registry (not the file: link).
  3. CI should then go green.

I deliberately did not pin this to 1.1.0 as a stopgap — that version lacks search(), so it would swap one red build for another while adding lockfile churn.

Locally (against the linked SDK): build clean, parity test passes with all 7 tools.

🤖 Generated with Claude Code

gradill22 and others added 6 commits July 20, 2026 16:45
- src/http.ts: a sibling to the stdio entry point that speaks MCP over
  Streamable HTTP, so hosted clients can connect without launching a
  local process. It calls the SAME createServer per request, so the tool
  surface is identical across transports. Dependency-free (raw node
  http). Runs stateless: access tokens expire hourly, so binding one to
  a long-lived session would break mid-session; per-request binding
  always uses the token just presented.
- OAuth resource-server role: serves /.well-known/oauth-protected-
  resource and answers unauthenticated or expired requests with 401 +
  WWW-Authenticate, which is what makes a client run (or refresh) the
  flow. A token is validated by one probe to the API, since an access
  token is just an API key; unreachable API fails closed with 503.
- search tool, mirroring the API's /search.
- test/: a zero-dependency (node:test) parity test asserting the stdio
  and HTTP entry points expose an identical tool list. CI now runs it —
  previously the repo had no test step at all.
- railway.toml to deploy the remote server as its own service.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a shared formatInputSchema (zod enum) to the three extraction tools.
`search` deliberately does not take one: it hands an agent N pages to
read, while the other formats serve pipelines that already know which URL
they want.

MCP tools return text, so renderContent() gives each format a legible
textual projection — an agent reading `[object Object]` is strictly worse
off than one reading markdown. Blocks and chunks keep their structure
visible (block type, token offsets), which is the reason a caller asked
for them. renderMetrics() adds a one-line tokens-saved summary when the
API reports it.

Build and the tool-parity test are clean.

NOTE: this repo's CI stays red for a reason unrelated to this change —
`wellmarked@^1.1.1` is unsatisfiable (npm has only 1.1.0) and the
lockfile still pins the SDK to file:../JS-TS-SDK. Locally that resolves
to the sibling checkout, which is why this builds here. See PR #13.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The remote Streamable-HTTP + OAuth server is a new feature since 1.0.0, so
this warrants a minor release. version.ts (reported in the MCP initialize
handshake) is hand-synced to package.json — kept in sync here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Product decision: no capability to point WellMarked tooling anywhere but
api.wellmarked.io. Both the stdio entrypoint and the remote Streamable-HTTP
server now hardcode the API base; the env var is gone from the docs. Also
tracks the SDK's removal of its baseUrl option (createServer forwards
options straight into new WellMarked()).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirrors the API: search takes the same extraction parameter set as extract/
bulk/crawl. renderSearch now goes through the shared renderContent path so
chunks/json/html/links results project to legible text instead of only
markdown. Format description notes the Pro+ gate on json/chunks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
retry: server-side re-attempts on target_timeout (fresh connection per
attempt, default 0). Shared schema across the three extraction tools;
deliberately kept off search — the API's 15s per-result deadline can't
absorb a retry, so the parameter doesn't exist there.

max_pages narrows the plan's crawl page cap (never widens it).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gradill22 gradill22 self-assigned this Jul 23, 2026
@gradill22
gradill22 merged commit 3519fc2 into main Jul 23, 2026
0 of 3 checks passed
@gradill22
gradill22 deleted the feat/remote-server-search branch July 23, 2026 05:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant