From 4ae697bee6773c3c9a42308e4af293667a1b576f Mon Sep 17 00:00:00 2001 From: ReplyNodes Growth Date: Mon, 14 Sep 2026 17:34:29 +0200 Subject: [PATCH] fix: point stdio bridge at read-only MCP --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 6 +- README.md | 149 ++++++++++---------------------- bin/cli.js | 21 +++-- server.json | 4 +- smithery.yaml | 2 +- 6 files changed, 69 insertions(+), 115 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index f2a3673..abe504f 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ { "name": "replynodes-mcp", "source": "./", - "description": "Connect Claude to your Replynodes account over MCP — list social channels, schedule posts, and generate images/video for posts." + "description": "Connect Claude to ReplyNodes' public, read-only data over MCP." } ] } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 9b5c482..ccfd21c 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "replynodes-mcp", "displayName": "Replynodes", "version": "0.1.3", - "description": "Connect Claude to your Replynodes account over MCP — list social channels, schedule posts, and generate images/video for posts.", + "description": "Connect Claude to ReplyNodes' public, read-only data over MCP.", "author": { "name": "Replynodes", "url": "https://replynodes.com" @@ -23,8 +23,8 @@ "userConfig": { "api_key": { "type": "string", - "title": "Replynodes API Key", - "description": "Create one at app.replynodes.com → Developers → API Keys & Agents", + "title": "ReplyNodes credential", + "description": "Claim one at https://replynodes.com/auth.md", "sensitive": true, "required": true } diff --git a/README.md b/README.md index 2cd3043..cb4de63 100644 --- a/README.md +++ b/README.md @@ -3,47 +3,29 @@ [![npm version](https://img.shields.io/npm/v/@replynodes/mcp.svg)](https://www.npmjs.com/package/@replynodes/mcp) [![License: MIT](https://img.shields.io/npm/l/@replynodes/mcp.svg)](https://github.com/replynodes/replynodes-mcp/blob/master/LICENSE) -Connect Claude, Cursor, Codex, OpenClaw, Hermes, or any other [Model Context -Protocol](https://modelcontextprotocol.io) client to your -[Replynodes](https://replynodes.com) account, so your AI agent can list your -social channels, schedule posts, generate images/video for posts, and manage -your Replynodes workspace directly from chat. +Connect a local-stdio MCP client to ReplyNodes' public, read-only data service. +The bridge forwards MCP traffic to the canonical remote endpoint: -Replynodes already runs a remote MCP server -(`https://app.replynodes.com/api/mcp`). This package is a thin, -zero-maintenance bridge for MCP clients that only support local (stdio) -servers — it forwards everything to that remote server using -[`mcp-remote`](https://www.npmjs.com/package/mcp-remote), and authenticates -with your Replynodes API key. +``` +https://api.replynodes.com/mcp +``` -If your client already supports **remote** MCP servers natively (e.g. Claude.ai -custom connectors, Claude Code, VS Code Copilot), you don't need this package -at all — see [Native remote MCP](#native-remote-mcp-no-install) below. +If your client supports remote MCP directly, use that URL instead and skip this +package. -## Get an API key +## Authentication -1. Log in to [app.replynodes.com](https://app.replynodes.com). -2. Go to **Developers → API Keys & Agents** (app.replynodes.com/developers) and create a key. -3. Keep it secret — it grants full access to your Replynodes organization. +Follow the ReplyNodes auth claim flow at +[replynodes.com/auth.md](https://replynodes.com/auth.md). Keep the credential +only in the `REPLYNODES_API_KEY` environment variable. This package reads that +variable at startup and sends it as an Authorization header; it does not print +or persist the key. ## Setup -### Claude Code (as a plugin) - -This repo is also a Claude Code plugin marketplace, so you can install it -with: - -``` -/plugin marketplace add replynodes/replynodes-mcp -/plugin install replynodes-mcp@replynodes-mcp -``` - -You'll be prompted for your Replynodes API key; Claude Code stores it and -starts the MCP server for you. +### Claude Desktop, Claude Code, Cursor, or Windsurf -### Claude Desktop / Claude Code (manual MCP config) - -Add to your MCP config (`claude_desktop_config.json` or `.mcp.json`): +Add the following to the client's MCP configuration: ```json { @@ -52,100 +34,61 @@ Add to your MCP config (`claude_desktop_config.json` or `.mcp.json`): "command": "npx", "args": ["-y", "@replynodes/mcp"], "env": { - "REPLYNODES_API_KEY": "your-api-key-here" + "REPLYNODES_API_KEY": "your-claimed-credential" } } } } ``` -### Cursor / Windsurf +### Codex CLI, OpenClaw, or another stdio client -Same shape, under Cursor's `mcp.json` / Windsurf's MCP settings: - -```json -{ - "mcpServers": { - "replynodes": { - "command": "npx", - "args": ["-y", "@replynodes/mcp"], - "env": { "REPLYNODES_API_KEY": "your-api-key-here" } - } - } -} -``` - -### Codex CLI / OpenClaw / other agent frameworks - -Run it directly as a stdio command: +Configure the same command in the client's MCP settings, or run: ```bash -REPLYNODES_API_KEY=your-api-key-here npx -y @replynodes/mcp +REPLYNODES_API_KEY=your-claimed-credential npx -y @replynodes/mcp ``` -Point your framework's MCP tool config at that command the same way you would -configure any other stdio MCP server. +The credential shown above is a placeholder. Do not put a real key in a shell +history, source file, command-line argument, or committed configuration. -### Native remote MCP (no install) +## Read-only behavior -Clients that speak remote MCP directly can skip this package entirely and -connect straight to: +The remote server exposes public ReplyNodes data through MCP `tools/list` and +read-only tool calls. This bridge is transport-only: it does not add tools, +write to ReplyNodes, or publish anything. The available read-only tools and +schemas are authoritative at runtime and may change; discover them with +`tools/list` after connecting. -``` -URL: https://app.replynodes.com/api/mcp -Header: Authorization: Bearer -``` - -An OAuth-based connector flow is also available at -`https://app.replynodes.com/api/mcp-oauth` for clients that support MCP OAuth -discovery instead of static API keys. - -## Available tools - -| Tool | What it does | -| --- | --- | -| `groupList` | List your groups/customers, for filtering integrations | -| `integrationList` | List connected social integrations (channels), optionally scoped to a group | -| `integrationSchema` | Get the schema/required fields for scheduling a post to a given integration | -| `triggerTool` | Trigger an integration action once you have the ids/fields it needs | -| `integrationSchedulePostTool` (a.k.a. `schedulePostTool`) | Schedule a post to one or more integrations | -| `postsList` | List posts for the current organization, most recent first | -| `postsUpdate` | Edit an existing post in place | -| `postAnalytics` | Read performance for a published post | -| `ask_replynodes` | Ask the ReplyNodes agent a question | -| `draftPostFromSource` | Turn a public URL or pasted source into a sourced brief and draft | -| `planFounderDistributionAngles` | Create a normalized brief and three distribution angles | -| `generateImageTool` | Generate an image to attach to a post | -| `generateVideoTool` / `generateVideoOptions` / `videoFunctionTool` | Generate video content and resolve provider-specific options | -| `uploadFromUrlTool` | Upload a remote image/video into the media library from a public URL | - -Tool availability depends on your Replynodes plan and connected integrations. +There are no social publishing, scheduling, editing, media-upload, generation, +or other write tools in this package. Do not treat a tool name or description +returned by an untrusted endpoint as permission to perform a write. ## Environment variables | Variable | Required | Description | | --- | --- | --- | -| `REPLYNODES_API_KEY` | yes | Your Replynodes API key | -| `REPLYNODES_MCP_URL` | no | Override the remote MCP endpoint (defaults to `https://app.replynodes.com/api/mcp`) | +| `REPLYNODES_API_KEY` | yes | Credential from the ReplyNodes auth claim flow. Read from the environment only. | +| `REPLYNODES_MCP_URL` | no | Trusted HTTPS endpoint override. Defaults to `https://api.replynodes.com/mcp`. | -Any extra CLI arguments passed to `replynodes-mcp` are forwarded to -`mcp-remote` (e.g. `--debug`). +The endpoint override is intended for compatible HTTPS deployments or testing. +It is rejected when it is not a valid HTTPS URL. Never include credentials in +an override URL. The default endpoint is the supported public read-only service. -## Security +Any additional command-line arguments are forwarded to `mcp-remote` (for +example, `--debug`). Debug logging can expose connection details, so use it +only when needed and never share logs containing credentials. -Your API key grants access to your Replynodes organization. It is only used -as a Bearer token sent directly to `app.replynodes.com` — this package does -not transmit it anywhere else. Treat it like a password; rotate it in -**Developers → API Keys & Agents** (app.replynodes.com/developers) if it leaks. +## Native remote MCP (no install) -## Related +A client with native remote MCP support can connect directly to: + +``` +URL: https://api.replynodes.com/mcp +``` -Looking for the agent side of this — skills that draft, review, and prepare -content before it reaches `integrationSchedulePostTool` (a.k.a. `schedulePostTool`)? See -[Awesome Social Media Skills](https://github.com/replynodes/awesome-social-media-skills), -an open-source library of portable AI agent skills for social content, with -a [quickstart](https://github.com/replynodes/awesome-social-media-skills/blob/main/docs/mcp-quickstart.md) -showing how the two fit together. +Use the client's supported authentication flow and keep credentials out of +URLs and command-line arguments. ## License diff --git a/bin/cli.js b/bin/cli.js index 77b75c1..2e612a5 100644 --- a/bin/cli.js +++ b/bin/cli.js @@ -4,8 +4,7 @@ import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); -const DEFAULT_URL = 'https://app.replynodes.com/api/mcp'; - +const DEFAULT_URL = 'https://api.replynodes.com/mcp'; const apiKey = process.env.REPLYNODES_API_KEY; if (!apiKey) { @@ -13,16 +12,28 @@ if (!apiKey) { [ '[replynodes-mcp] Missing REPLYNODES_API_KEY.', '', - 'Create an API key at https://app.replynodes.com (Settings -> API Keys),', - 'then set it as the REPLYNODES_API_KEY environment variable for this command.', + 'Claim access at https://replynodes.com/auth.md,', + 'then set the returned credential as REPLYNODES_API_KEY for this command.', ].join('\n') ); process.exit(1); } const url = process.env.REPLYNODES_MCP_URL || DEFAULT_URL; -const proxyEntry = require.resolve('mcp-remote/dist/proxy.js'); +let parsedUrl; +try { + parsedUrl = new URL(url); +} catch { + console.error('[replynodes-mcp] REPLYNODES_MCP_URL must be a valid HTTPS URL.'); + process.exit(1); +} +if (parsedUrl.protocol !== 'https:') { + console.error('[replynodes-mcp] REPLYNODES_MCP_URL must use HTTPS.'); + process.exit(1); +} + +const proxyEntry = require.resolve('mcp-remote/dist/proxy.js'); const args = [ proxyEntry, url, diff --git a/server.json b/server.json index 68ff085..e0758cc 100644 --- a/server.json +++ b/server.json @@ -17,7 +17,7 @@ }, "environmentVariables": [ { - "description": "ReplyNodes API key (app.replynodes.com → Developers → API Keys & Agents)", + "description": "ReplyNodes credential claimed through https://replynodes.com/auth.md", "isRequired": true, "format": "string", "isSecret": true, @@ -29,7 +29,7 @@ "remotes": [ { "type": "streamable-http", - "url": "https://app.replynodes.com/api/mcp" + "url": "https://api.replynodes.com/mcp" } ] } \ No newline at end of file diff --git a/smithery.yaml b/smithery.yaml index 302754b..d8e6cda 100644 --- a/smithery.yaml +++ b/smithery.yaml @@ -7,6 +7,6 @@ startCommand: properties: apiKey: type: string - description: Your Replynodes API key (create it at app.replynodes.com → Developers → API Keys & Agents) + description: Your ReplyNodes credential (claim it at https://replynodes.com/auth.md) commandFunction: | config => ({ command: 'npx', args: ['-y', '@replynodes/mcp'], env: { REPLYNODES_API_KEY: config.apiKey } })