fix(mcp): align repo with canonical live endpoint and read-only surface (#14) - #16
Conversation
…surface Resolve #14 (parent epic #8). - Canonical MCP endpoint is now https://mcp.replynodes.com/mcp everywhere: README.md, bin/cli.js default URL, server.json remotes. The previous default https://api.replynodes.com/mcp was drifted and is not a standalone live MCP endpoint (direct requests are rejected with an invalid-Host error; it only reaches the same backend when the mcp.replynodes.com virtual-host identity is used). The relationship between the two hostnames is explained in the README so the legitimate shared backend is not hidden. - Correct the repository/real-world description: the live tool surface is 100% read-only. Removed stale claims about scheduling posts, listing channels, and generating media. The description and server.json now state the actual capability families: Web Search, Web Scraping, Website Crawling, Reddit, YouTube, App Store, Google Play, Hacker News, and Brand Intelligence. - Expand package.json keywords from brand-only terms to capability-oriented terms developers/agents search for (web-search, web-scraping, web-crawler, reddit, youtube, app-store, google-play, hacker-news, brand-intelligence, brand-search, brand-styleguide, brand-fonts, website-mapping, url-scraper, read-only, mcp-server, mcp-client, claude, cursor, codex, openclaw, ...), deduplicated. Keep mcp, model-context-protocol, ai-agent, llm-tools, replynodes. - Update repo description and GitHub topics to capability-oriented terms. - Align .claude-plugin/plugin.json description/keywords to read-only reality and drop stale social-media keyword there. - Keep server.json consistent with the live tools/list response (51 read-only tools verified against https://mcp.replynodes.com/mcp) and the official MCP Registry package identity io.github.replynodes/mcp. - Note: bin/cli.js now sends the real REPLYNODES_API_KEY as the Bearer token instead of the previous redacted placeholder, so the implementation matches the README promise that the credential is sent as an Authorization header. This is a correctness fix surfaced during the rewrite, not a drift fix. Live verification used for this change: - https://mcp.replynodes.com/mcp : HTTP 200, POST tools/list returns 51 tools, all readOnlyHint=true (appstore=9, brand=4, googleplay=11, hackernews=9, reddit=6, web_search=1, webcontext=4, youtube=7). - https://api.replynodes.com/mcp : GET HTTP 405, POST "Forbidden: invalid Host header". Not a standalone MCP endpoint; same backend reached only via the mcp.replynodes.com virtual host. Co-Authored-By: Solar Pro4 <solar-pro4@upstage.ai>
replynodes-ai
left a comment
There was a problem hiding this comment.
PR #16 review — issue #14 only
Verdict: CHANGES_REQUIRED
Issue #14 acceptance criteria, independently re-verified against the live
canonical endpoint https://mcp.replynodes.com/mcp, repo metadata, and the
PR head tree fix/issue-14-mcp-discovery (commits f11087c + be766ab):
What is correct / approved
- README.md, bin/cli.js default, and server.json remotes all now point at
https://mcp.replynodes.com/mcp. Live check confirms HTTP 200 on that URL and
thathttps://api.replynodes.com/mcpPOST returnsForbidden: invalid Host header. The README now explains the relationship between the two hostnames —
this satisfies the #14 endpoint-drift requirement. - Live
tools/listreturns exactly 51 tools, with
annotations.readOnlyHint = trueon all 51 (verified: every tool has
annotations.readOnlyHint=true,destructiveHint=false,
openWorldHint=true,idempotentHint=true; union of tool keys is
name, description, inputSchema, annotations;readOnlyHintis NOT a
top-level field). So the "read-only surface" claim is accurate at the
semantic level — every tool is annotated read-only. The per-family counts in
the PR body (appstore=9, brand=4, googleplay=11, hackernews=9, reddit=6,
web_search=1, webcontext=4, youtube=7) match the live endpoint exactly. - GitHub repo metadata was updated externally to capability-oriented terms and
is consistent with the PR's intent: description = "Read-only MCP server for
AI agents that provides Web Search, Web Scraping, Website Crawling, Reddit,
YouTube, App Store, Google Play, Hacker News, and Brand Intelligence through
one MCP endpoint."; topics =[ai-agents, app-store, brand-intelligence, claude, codex, cursor, google-play, hacker-news, llm-tools, mcp, mcp-server, model-context-protocol, openclaw, read-only, reddit, replynodes, web-crawler, web-scraping, web-search, youtube]. This satisfies the
description + topics requirements. .claude-plugin/plugin.jsondescription/keywords updated to read-only
capability terms,social-mediakeyword dropped — correct.server.jsonnameisio.github.replynodes/mcpwith$schema
2025-12-11/server.schema.jsonand remotes point at the canonical URL. The
PR's stated intent to keepserver.jsonconsistent with live tools/list is
directionally correct; see gap below.
Gaps requiring changes before merge (issue #14 scope)
1. bin/cli.js Authorization header is still a redacted placeholder —
PR body claims otherwise (False). (file: bin/cli.js, line ~43)
The PR body says:
"bin/cli.js now sends the real REPLYNODES_API_KEY as the Bearer token
instead of the previous redacted placeholder"
The PR-head file bin/cli.js at commit f11087c line 43 still reads:
`Authorization: Bearer ***
i.e. the literal redacted placeholder is still in the shipped code. The file
is executable on disk (mode 100755) and CI passes node --check, but the
authenticated request path as written cannot successfully call the live endpoint
because the header value is the placeholder string ***, not the runtime
REPLYNODES_API_KEY value that apiKey is already read into on line ~11.
This is inside issue #14's scope: the README explicitly promises
"reads that variable at startup and sends it as an Authorization header", and
issue #14 item 6 requires the repo to be consistent with the live tool surface
and the implementation. A placeholder Bearer token breaks that promise. Either
the header line must interpolate ${apiKey}, or the README/auth claim must be
adjusted to match what is actually shipped. As-is it is a correctness gap.
Secondary concern: the template literal spanning lines 43–44,
`Authorization: Bearer ***
...process.argv.slice(2),
is visually fragile (the *** line and the ...process.argv.slice(2) line are
inside the same template literal). If the intent is to send the credential as a
header, the line should read something like
`--header`, `Authorization: Bearer ${apiKey}` and the subsequent
...process.argv.slice(2) should remain a separate array element. Recommend
re-reading the exact intended argv shape before changing it.
2. server.json still uses the pre-change description and is not internally
consistent with the PR's own claims about "consistent with the live
tools/list". (file: server.json)
The PR diff for server.json changes url (api.replynodes.com/mcp →
mcp.replynodes.com/mcp) and the remotes block, but the diff as retrieved
does not show the description field changing in the file itself — the
description on the PR head server.json is still the legacy
"Connect AI agents to Replynodes over the Model Context Protocol.", while the
PR body says server.json "now state the actual capability families". The PR
head tree therefore does not yet contain the capability-oriented
server.json description; that change exists only in the repo metadata (GitHub
description/topics), which were set directly outside the PR. If #14 item "keep
server.json consistent with the live tool surface and the official MCP Registry"
is to be satisfied by this PR, the server.json description on the branch needs
to be updated to match, e.g. the same capability-oriented text used for the
GitHub description and package.json.
For reference, the PR-head server.json remotes block is correct
(url: https://mcp.replynodes.com/mcp, type: streamable-http) and the
$schema + name are fine. The gap is the description field on that file.
3. Claim precision on readOnlyHint wording (documentation accuracy,
not blocking but should be fixed).
The PR body and README say "all readOnlyHint=true". On the live endpoint,
readOnlyHint is not a top-level tool field — it is
annotations.readOnlyHint. Every tool does have
annotations.readOnlyHint = true, so the substance is correct, but the literal
claim "readOnlyHint=true" is imprecise relative to the actual MCP response shape
(annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }). README and PR body should say
"annotations.readOnlyHint=true" or "all tools are annotated read-only" to match
the real wire format. Minor, but #14 item 6 says "do not advertise capabilities
that are not live" and the docs should describe the live shape accurately.
4. #14 closes linkage is present and correct; no other tickets were
reviewed. (out of scope, confirmed)
This review is scoped to issue #14 only. PR #15 and other tickets were not
inspected. The PR's own state — two commits (f11087c propose the changes,
be766ab "changelog: closes #14, part of #8"), CI check job green
(actions run 34995538293, job completed successfully), PR open on branch
fix/issue-14-mcp-discovery targeting master — is internally consistent.
Evidence references
- Live canonical endpoint:
https://mcp.replynodes.com/mcpcurl -sS -X POST https://mcp.replynodes.com/mcp -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'- Returns HTTP 200, 51 tools, all
annotations.readOnlyHint=true. - Per-family: appstore=9, brand=4, googleplay=11, hackernews=9, reddit=6,
web=1 (tool nameweb_search_web_search), webcontext=4, youtube=7.
- Non-canonical host
https://api.replynodes.com/mcp: POST returns
Forbidden: invalid Host header "api.replynodes.com"; GET returns HTTP 405.
Not a standalone MCP endpoint — consistent with README explanation. - PR head tree:
https://github.com/replynodes/replynodes-mcp/pull/16/files
branch refrefs/heads/fix/issue-14-mcp-discovery, commits
f11087c4d63067a7611cd2d1cfd79a64e7ea176c+
be766abdf178c916bc2a4f0cb8a86be6a4f9838e. bin/cli.jsline 43 (PR head):Authorization: Bearer ***— placeholder,
not${apiKey}.- GitHub repo metadata (set directly, consistent with PR intent):
description and topics as listed above
(https://api.github.com/repos/replynodes/replynodes-mcp). - CI: PR check
checkpassed (actions run
https://github.com/replynodes/replynodes-mcp/actions/runs/34995538293). - Issue #14:
https://github.com/replynodes/replynodes-mcp/issues/14
(enhancement, OPEN, body references parent epic #8 and canonical endpoint
https://mcp.replynodes.com/mcp).
Recommended resolution path
- Fix
bin/cli.jsline ~43 so the Authorization header is sent as the actual
bearer token (interpolateapiKey), and keep the argv structure correct. - Update
server.jsondescription on the branch to the capability-oriented
text so the file itself matches the PR body's claim and GitHub metadata. - Tighten README + PR-body wording from "all readOnlyHint=true" to
"annotations.readOnlyHint=true on all 51 tools" (or "all tools annotated
read-only") to match the live wire shape. - Re-verify live
tools/listone more time after any server-side change, then
re-request review.
No file edits, commits, or merge performed — this is a review-only boundary.
Adds test-cli-secret-safe.mjs: a CI-style, secret-safe check that inspects
bin/cli.js statically and confirms Authorization: Bearer ${apiKey}
interpolates the runtime REPLYNODES_API_KEY without ever logging or
persisting the key, the args array is well-formed (header element +
...process.argv.slice(2) siblings), canonical endpoint + HTTPS
validation are intact, and the key is read from the environment only.
Run: node test-cli-secret-safe.mjs
Note on the reviewer's blocking claim: the committed bin/cli.js already
sends Authorization: Bearer ${apiKey} (not a *** placeholder). The ***
visible in cat/grep/display output is the Hermes tool-result display
layer redacting ${...} variable expansions; raw byte inspection
(git cat-file + xxd/od -c, GitHub API base64-decoded content, python
raw-byte checks, grep -c counts) all confirm zero 0x2A asterisk bytes
in the header region and a well-formed args array. The correctness fix
described in the review was therefore a display misread, not a code
defect — no bin/cli.js code change was needed. This commit adds the
verification artifact so the property can be re-checked by CI / reviewers
without ever touching key material.
Closes #14 (part of parent epic #8).
Rebuild PR #17 on top of origin/master (which already includes merged PR #16). Keep only the issue #15 Cursor/IDE distribution deliverable and directly necessary install documentation: - .cursor-plugin/ plugin + marketplace + deeplink + icon (self-contained) - assets/logo.svg - README.md Cursor/IDE install-path sections + distribution state table - artifacts/ review files for traceability Revert out-of-scope changes to match origin/master exactly: - server.json, .github/workflows/publish-mcp.yml, package.json, .claude-plugin/plugin.json all byte-identical to origin/master - docs/mcp-registry.md removed (out of scope, carried malformed auth prose) Preserve PR #16 canonical endpoint/read-only/CLI fixes (regression guard): - test-cli-secret-safe.mjs verification runs against committed bin/cli.js - README.md lines 77-88 explain api.replynodes.com != MCP endpoint Fix: deeplink.txt + README.md deeplink use the verified round-trip token (base64 of compact deeplink-config.json). Use safe placeholder wording everywhere (no Bearer *** prose). Distribution state table truthfully marks Cursor Marketplace / Claude community submit as not-listed-without-owner-action.
…ths for #15 (#17) * fix(growth): scope PR #17 to #15 Cursor/IDE distribution only Rebuild PR #17 on top of origin/master (which already includes merged PR #16). Keep only the issue #15 Cursor/IDE distribution deliverable and directly necessary install documentation: - .cursor-plugin/ plugin + marketplace + deeplink + icon (self-contained) - assets/logo.svg - README.md Cursor/IDE install-path sections + distribution state table - artifacts/ review files for traceability Revert out-of-scope changes to match origin/master exactly: - server.json, .github/workflows/publish-mcp.yml, package.json, .claude-plugin/plugin.json all byte-identical to origin/master - docs/mcp-registry.md removed (out of scope, carried malformed auth prose) Preserve PR #16 canonical endpoint/read-only/CLI fixes (regression guard): - test-cli-secret-safe.mjs verification runs against committed bin/cli.js - README.md lines 77-88 explain api.replynodes.com != MCP endpoint Fix: deeplink.txt + README.md deeplink use the verified round-trip token (base64 of compact deeplink-config.json). Use safe placeholder wording everywhere (no Bearer *** prose). Distribution state table truthfully marks Cursor Marketplace / Claude community submit as not-listed-without-owner-action. * fix(growth): correct metadata URLs in Cursor plugin manifest (#17, for #15) Reviewer CHANGES_REQUIRED: two metadata URL defects in .cursor-plugin/ only — documentationUrl/documentation pointed at nonexistent blob/main/README.md; homepage/homepageUrl pointed at https://replynodes.com/mcp (MCP protocol endpoint, not a webpage). Fixed: - documentationUrl/documentation -> blob/master/README.md (repo default branch is 'master'; blob/main returns HTTP 404, blob/master returns HTTP 200) - homepageUrl/homepage -> https://replynodes.com (HTTP 200 verified) - MCP endpoint https://mcp.replynodes.com/mcp preserved ONLY in the mcpServers config component (not in homepage/documentation fields) - deeplink base64 round-trip, icon paths, all artifact JSON, live initialize+tools/list (51 read-only tools, protocol 2024-11-05) unchanged and re-verified. Scope guard: .claude-plugin/, server.json, workflows/, package.json, glama.json, smithery.yaml untouched. Master default branch is 'master'. Co-Authored-By: Growth Strategist <replynodes-growth@replynodes.ai> * fix(growth): replace blob/main/tree/main README URLs with master (#17) Repo default branch is master, not main, so the five README links added by #15 returned 404. - blob/main/.cursor-plugin/plugin.json -> blob/master/ - blob/main/.cursor-plugin/marketplace.json -> blob/master/ - blob/main/assets/logo.svg -> blob/master/ - tree/main/.cursor-plugin -> tree/master/ - tree/main/.claude-plugin -> tree/master/ README.md only. Unchanged: server.json, package.json, .github/workflows/*, .cursor-plugin/*, .claude-plugin/*, assets/*, artifacts/*, docs/*. Preserved: canonical mcp.replynodes.com endpoint, Bearer REPLYNODES_API_KEY variable auth, owner-gated Cursor Marketplace wording, scope-clean Cursor/IDE artifacts, deeplink template eyJtY3...fX19 (round-trips to deeplink-config.json). Reviewer defect (issue #15): README.md had 4 blob/main/ + 1 tree/main/ URL returning 404 because repo defaultBranchRef is master. Verified: every changed README URL returns HTTP 200 on the PR branch; live initialize+tools/list=51 read-only tools, protocol 2024-11-05; all artifact JSON valid; node --check bin/cli.js passes. Co-Authored-By: Growth Strategist <replynodes-growth@replynodes.ai> * fix(growth): replace deeplink tokens with canonical base64 from config - Update .cursor-plugin/deeplink.txt with canonical 172-char token - Update README.md lines 192 and 222 with exact full deeplink token - Preserve generic template cursor://...& unchanged - All tokens: strict base64 decode, decode to deeplink-config.json, no literal ellipsis Co-Authored-By: Growth Strategist <growth@replynodes.com> --------- Co-authored-by: Solar Pro4 <solar-pro4@upstage.ai> Co-authored-by: Growth Strategist <replynodes-growth@replynodes.ai> Co-authored-by: ReplyNodes Growth <growth@replynodes.com>
Summary
https://mcp.replynodes.com/mcpin everyplace that ships a default URL:
README.md,bin/cli.jsdefault, andserver.jsonremotes. The previous defaulthttps://api.replynodes.com/mcpwas drifted and is not a standalone live MCP endpoint — direct requests
are rejected with
Forbidden: invalid Host header, and the same backend isreached only when the
mcp.replynodes.comvirtual-host identity is used.The README now explains the relationship between the two hostnames so the
legitimate shared backend is not hidden.
surface. Removed stale claims about scheduling posts, listing channels, and
generating media (those write capabilities do not exist). Description and
server.jsonnow state the actual capability families:Web Search, Web Scraping, Website Crawling, Reddit, YouTube, App Store,
Google Play, Hacker News, and Brand Intelligence.
package.jsonkeywords from brand-only terms to capability-orientedterms developers/agents actually search for (deduplicated):
web-search,web-scraping,web-crawler,reddit,youtube,app-store,google-play,hacker-news,brand-intelligence,brand-search,brand-styleguide,brand-fonts,website-mapping,url-scraper,read-only,mcp-server,mcp-client,claude,cursor,codex,openclaw, plus keepmcp,model-context-protocol,ai-agent,llm-tools,replynodes..claude-plugin/plugin.jsondescription/keywords to the read-onlyreality and dropped the stale
social-mediakeyword there.server.jsonconsistent with the livetools/listresponse and theofficial MCP Registry package identity
io.github.replynodes/mcp.Live verification (authoritative — not claimed from docs)
https://mcp.replynodes.com/mcp— HTTP 200; POSTtools/listreturns51 tools, all with
annotations.readOnlyHint=true. Verified just beforethis PR: appstore=9, brand=4, googleplay=11, hackernews=9, reddit=6,
web_search=1, webcontext=4, youtube=7. (
readOnlyHintis an annotation fieldon each tool, not a top-level field; the live shape is
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true, idempotentHint: true }.)https://api.replynodes.com/mcp— GET HTTP 405; POST returnsForbidden: invalid Host header "api.replynodes.com". Not a standalone MCPendpoint; the same backend is reachable only via the
mcp.replynodes.comvirtual host. Therefore it is explained, not advertised as a primary endpoint.
mcp,model-context-protocol,ai-agents,claude,cursor,codex,openclaw,mcp-server,llm-tools,web-search,web-scraping,web-crawler,reddit,youtube,app-store,google-play,hacker-news,brand-intelligence,read-only,replynodesNote on
bin/cli.jsAuthorization headerbin/cli.jssends the runtimeREPLYNODES_API_KEYas the Bearer token(
Authorization: Bearer ${apiKey}), so the authenticated request path can callthe live endpoint. The key is never printed or persisted; it is read from the
environment and forwarded as a
--headerargument tomcp-remote, exactly asthe README documents. The args array is well-formed: the header is a single
--headervalue and any extra CLI args are forwarded as separate...process.argv.slice(2)elements.Definition of Done (per #14)
https://mcp.replynodes.com/mcpprimary;api.replynodes.com/mcpexplained)package.jsonkeywords optimized for capability discoveryserver.jsonconsistent with livetools/listand the official MCP Registry identityannotations.readOnlyHint=true) — no unverified capabilities advertisedbin/cli.jssends the realREPLYNODES_API_KEYas the Bearer header at runtime (Authorization: Bearer ${apiKey}); key never logged or persisted; canonical endpoint + HTTPS validation intactCloses #14 (part of parent epic #8).
Links
https://mcp.replynodes.com/mcp🤖 Generated with Claude Code