EchoRelay
A managed runtime for custom API integrations. Manage lines, endpoints, keys, logs and DLQ via MCP.
Connect it
Settings → Connectors → Add custom connector
URL: https://mcp.echorelay.devSettings → Apps & Connectors → Advanced → Developer mode
Create connector with URL: https://mcp.echorelay.dev{
"mcpServers": {
"management": {
"url": "https://mcp.echorelay.dev",
"headers": {
"Authorization": "<YOUR_AUTHORIZATION>"
}
}
}
}claude mcp add --transport http management https://mcp.echorelay.dev --header "Authorization: <value>"{
"servers": {
"management": {
"type": "http",
"url": "https://mcp.echorelay.dev",
"headers": {
"Authorization": "<YOUR_AUTHORIZATION>"
}
}
}
}80 tools, pulled live
get_projectread-onlyGet the EchoRelay project this token is scoped to: id, slug, name, caller-facing API base URL, whether the token has edit access, and the request-log hot-tier retention window.
list_linesread-onlyList every line in the project: returns `{lines: []}` in one call, with no paging. Each entry reports `lineKey`, `name`, `active`, `docsShared`, `publishedAt`, `endpointCount`, and its draft state (`draftRevision`, `hasDraft`, `scheduledPublish`). A project with no lines yet returns an empty `lines` array rather than an error.
list_endpointsread-onlyList the endpoints under one line, identified by `lineKey`; returns `{lineKey, endpoints: []}` with no paging. Each entry reports `id`, `name`, `path`, `httpMethod`, `strict`, `mock`, and `targetCount` — not the endpoint's full body, which get_endpoint returns. Errors when `lineKey` is missing or doesn't match a line in the project.
get_endpointread-onlyGet one endpoint's full document by `lineKey` and `endpointId`: `id`, `lineKey`, `name`, `url`, `httpMethod`, `strict`, `rateLimit`, `allowedIPs`, `idempotencyEnabled`, `senderSignature` (`{scheme, credentialId}`, plus `custom` for the custom-hmac scheme, or null: whether the sender's signature is verified, never the secret), `mock`, `mockResponse`, `attributes`, `targets` (auth redacted), and `tr
get_configread-onlyGet the project's published relay configuration — the exact JSON the EchoRelay Framework consumes. Includes pendingPublish with its frozen, redacted config when one is scheduled.
config_diffread-onlyPreview what Save & Publish (or the pending scheduled publish, if one exists) would change: added/removed/changed lines, and within each changed line the added/removed/changed endpoints and targets, plus any project-level setting change. Outbound auth secrets are redacted the same as get_config.
list_config_revisionsread-onlyList every retained revision of one line, newest first, so a target revision can be picked for rollback_config. Returns `{revisions: []}` with no paging; each entry has `revision`, `publishedAt`, `current`, and `active`. Errors when `lineKey` is missing or doesn't match a line in the project.
get_line_draftread-onlyRead one line draft, its optimistic revision, preview, and schedule.
preview_line_draftread-onlyPreview one line draft without publishing it.
publish_line_draftcan modifyPublish one line draft now and clear only its schedule.
discard_line_draftcan modifyDiscard every unpublished change and schedule for one line.
discard_draft_endpointcan modifyDiscard one endpoint change from a line draft.
schedule_line_publishcan modifySchedule one non-empty line draft. Active subscribers required.
cancel_line_publishcan modifyCancel one line schedule before its two-minute lock.
rollback_configcan modifyPreview or apply publishing a retained line revision over what is currently published, clearing any pending schedule. Without confirmRevision this only returns the diff (the redacted change-gate preview, same shape as config_diff) between what is currently published and the target revision — it changes nothing. To apply, call again with confirmRevision set to the diff's currentRevision; if a publi
get_billingread-onlyGet the project's credit balances (paid + testing) and which ledger it settles on. `billingModel` is `prepaid` (metered against the credit balance) or `invoiceable` (an Enterprise account, settled on the contract — its credit fields read empty/zero by design, not because it is out of credits). A zero balance is not on its own a reason to stop: live traffic is never refused for an empty balance, an
set_burst_opt_incan modifyEnable or disable the auto-decaying burst window for live traffic and publish the change immediately. When enabled, brief spikes a little above your steady RPM cap are absorbed at the normal credit rate (no surcharge, no burst SKU) instead of being rejected. Sustained overage decays back to your steady cap; headroom returns once traffic cools below the cap or you add RPM. Burst usage is tracked so
set_rpm_ceilingcan modifySet or clear a self-set ceiling on this project's own effective live-traffic RPM (Traffic shaping) — for when your origin can't take the throughput your plan grants. Never plan-gated: the ceiling can only LOWER your effective RPM below your plan/add-on rate, never raise it, and never touches billing. Steady traffic is capped at the ceiling; if Burst Protect is on, its headroom is measured above th
set_outbound_allowlistcan modifyNarrow the project's outbound host allowlist — the hosts a target may point at. The current list is on get_project as `outboundHostAllowlist` (empty means any public host). This tool may only narrow it: `patterns` must be a strict subset of, or equal to, the current list, compared as exact strings — submitting a wildcard that would cover an existing exact host, or clearing to empty, is refused as
create_lineCreate a new versioned line. A new line has no endpoints, so creating one publishes nothing and serves no traffic yet — add an endpoint, which publishes the line with it. Draft mode requires draftRevision 0. Requires edit access.
delete_linecan modifyDelete a line and every endpoint under it, immediately or by staging the removal in the line's draft with commitMode "draft" and draftRevision (returns `{draft}`, discardable via discard_line_draft, instead of deleting right away). An immediate delete removes the line's own revision history along with it, so list_config_revisions has nothing left to list and rollback_config cannot bring the line b
activate_lineActivate and immediately publish an inactive line so it serves traffic again. Blocked at the active-line cap — deactivate another line first. Requires edit access.
deactivate_linecan modifyTake a line off the data plane immediately (callers get 404) without deleting it — endpoints/targets/config are preserved and it can be reactivated later. A deliberate action; live traffic is never auto-disabled. Requires edit access.
duplicate_lineCopy a line and all its endpoints/targets/rules into a new line key — build v2 from v1. The copy is saved inactive and remains off the data plane; edit it, then activate_line when ready. Requires edit access.
set_docs_sharedcan modifyTurn the line's public OpenAPI docs on or off. When enabled, the line's spec is published at docs.{baseDomain}/{slug}/{lineKey} (and …/openapi.json); when disabled those URLs return 404. The `docsShared` field on each line read (list_lines) reflects the current state. Requires edit access.
create_endpointCreate an endpoint under a line. Defaults to immediate publish; draft mode requires draftRevision. scheduledAt stages the line draft then schedules it. Requires edit access.
generate_line_draftGenerate a draft endpoint under a line from a sample inbound request and a description of the target: infers the inbound body schema from the sample, reads the target's method/URL/fields from an OpenAPI document (targetDocument) or a bare sample plus targetUrl, and proposes an outgoingMapping pairing every target field to a same-named inbound field. Nothing is guessed — a target field with no same
update_endpointcan modifyUpdate an endpoint immediately or by staging the change in its line draft with commitMode "draft" and draftRevision; the patch is merged field-by-field onto the endpoint's current values, so omitted keys keep their values. Targets are matched by the `id` get_endpoint returned; without one, by the same `name` and `targetUrl` (when only one stored target has that pair); without either, a patch to th
delete_endpointcan modifyDelete an endpoint and its targets, immediately or by staging the removal in the line's draft with commitMode "draft" and draftRevision (returns `{draft}` instead of deleting right away). An immediate delete publishes a new revision without the endpoint and returns `{deleted: true, endpointId}`, but the line's prior revision still has it, so list_config_revisions can find that revision and rollbac
default_endpoint_templateread-onlyReturn a canonical minimal-valid endpoint JSON for a fresh line. POST it verbatim to create_endpoint to land an immediately-working endpoint, then customise. It comes back with `mock: true`: it answers callers with its own canned body and never dials the target it carries, so a 200 from it proves the endpoint is reachable and nothing about delivery. Set `mock: false` and give the target a real URL
dry_run_endpointread-onlyValidate an endpoint document without writing it. Returns either {ok: true, resolved: <Framework config slice>} or {ok: false, errors: {...}}. Pass endpointId to dry-run a PATCH against an existing endpoint; omit it to dry-run a create. Supply sample and/or answerSample to also run each target's mapping against a real payload and see what it produces, which field came from which node, and every ru
list_membersread-onlyList accepted and pending members of this project, plus the owner (role "owner"), each with `id`, `email`, `name`, `role`, and whether the invite has been accepted. Returns `{members: [], count, seatCap, seatsUsed}` with no paging — `count` is the roster size, `seatCap`/`seatsUsed` are the plan's seat limit and how many seats are used. Errors when the caller isn't the project owner or the token la
invite_memberInvite a person by email to collaborate on this project. Returns the new invitation record; the invitee receives an email with an accept link. Enforces the seat cap for the plan tier. Owner only.
remove_membercan modifyRemove a member (accepted or pending) from this project by memberId or email, matching the first record found; returns `{message, email}`. This deletes the membership record outright with no undo, so getting the person back onto the project takes a fresh invite_member call, not a restore. Errors when neither `memberId` nor `email` is given, no member matches, the caller isn't the project owner, or
set_member_rolecan modifyChange a member's role between editor, viewer, and billing. Identify the member by their member ID or email (from list_members); the project owner's own role cannot be changed. Returns the updated member record. Owner only.
resend_inviteRe-send the invitation email for a still-pending invite, by member ID or email (from list_members). The original accept link is reused. Already-accepted members are rejected. Owner only.
list_api_keysread-onlyList the project's inbound API keys as metadata only — the secret plaintext is never returned, only `id`, `name`, `mode`, `prefix`, `createdAt`, `createdBy`, `lastUsedAt`, `expiresAt`, `revokedAt`, `successorId`, `predecessorId`, and `successorRevealedAt`. Returns `{keys: []}` with no paging — every key on the project comes back in one call.
create_api_keyMint an inbound relay (data-plane) API key for callers to send traffic to this project. Its `er_live_` or `er_test_` secret authenticates relay requests; it cannot call management MCP or REST. Returns the plaintext exactly once — store it now, it cannot be retrieved later. Editor + owner; subject to the project key policy (see get_key_policy).
rotate_api_keycan modifyMint a linked successor for an existing key. Returns the successor plaintext exactly once — store it now. The predecessor stays valid through its overlap window so callers can swap without downtime. Owner anywhere, editor on own keys.
reveal_rotation_successorread-onlyOne-shot retrieval of the plaintext for a successor minted by the auto-rotation scheduler (not by rotate_api_key — that returns its own plaintext directly). Decrypts the at-rest ciphertext, returns it once, then destroys it; a second call errors. Only available while the predecessor still has an unrevealed successor.
revoke_api_keycan modifyRevoke a key immediately: it stops authenticating as soon as the change reaches the relay, with no overlap window, so callers still using it start failing. To replace a key without downtime, use rotate_api_key instead; if the key already has a successor, fetch it with reveal_rotation_successor, because rotate_api_key refuses a key that has one. Revoking a successor leaves its predecessor unable to
get_key_policyread-onlyReturn the project's key policy: `defaultKeyTtlDays` (what create_api_key uses when `ttlDays` is omitted), plus `maxKeyTtlDays` and `editorsMayCreateNonExpiringKeys`, the limits create_api_key enforces when minting a key. Read-only; use set_key_policy to change these values.
set_key_policycan modifyOwner-only. Edit the project's key policy. Only fields present in the patch are touched. Editors calling this get an error — use get_key_policy to read.
list_credentialsread-onlyList the project's saved credentials — reusable target-auth secrets that can be referenced from any number of targets instead of entering auth inline per target. `auth` is redacted (secret fields never returned, even encrypted — see create_credential for why).
create_credentialSave a new reusable credential, sealed with the project's encryption key at write time. Neither this call nor any later read ever returns the secret back — reference it from a target by id (see create_endpoint/update_endpoint's target.credentialVaultEntryId) instead of copying the secret around. `auth.type` selects which fields apply: bearer→token, basic→username+password, apiKeyHeader→headerName+
update_credentialcan modifyRename a credential and/or rotate its secret, publishing the change immediately so every target that uses this credential picks it up right away. Omit a secret field (or the whole `auth` object) to keep the existing value — the vault can never show a secret back to confirm it, so leaving it out means "unchanged," not "cleared." The destination host cannot be changed here: create a new credential f
delete_credentialcan modifyDelete a saved credential. Blocked (not a permission or not-found error — a validation error) while any target still references it; repoint or remove those targets first.
list_requestsread-onlyList inbound API requests the relay has processed for this project, newest first. Hot-tier window is the project's requestLogRetentionDays (see get_project). Optional filters narrow the result. Returns {total, limit, offset, rows[]} where each row has ts (unix ms), requestId, method, path, status, durationMs, targets, creditsDeducted, authType, errorMessage, bytesSent, configVersion (the published
get_requestread-onlyGet one inbound request envelope + every per-target delivery attempt made on the way out (success / permanent_failure / retry_scheduled / dlq). The request and each attempt carry configVersion, the published config that authorised them; 0 means unstamped. Scoped to this project; returns null once the request has aged out of the project's log-retention window (requestLogRetentionDays — see get_proj
get_receiptsread-onlyGet every target this request fanned out to, each with its delivery receipt or the reason it has none. A still-moving delivery (queued/retrying) answers from the live tracking copy, a terminal one (delivered/failed) from durable storage — the response shape is identical either way, so poll this on an interval with the same requestId a 202 response returned and watch `status` move to a terminal val
get_metricsread-onlyAggregate request metrics for this project over a time window — volume, error rate and p50/p95/p99 latency, bucketed for charting. By default the series cover inbound requests; pass `targetId` to slice over one outbound target's per-attempt rows instead (answers "which target is degraded?"); pass `endpointId` to slice over inbound rows for a single endpoint (answers "is this endpoint receiving tra
list_dlqread-onlyList entries currently in this project's dead-letter queue, newest first. One inbound request fans out per-target, so a single request may produce several DLQ entries, one per target that didn't accept it (different `targetId`s). Returns `{total, limit, offset, rows[], evictedCount}` where each row has `id` (the dead-letter entry id, `{ms}-{seq}`), `requestId`, `targetId`, `configVersion` (the pub
get_dlq_entryread-onlyGet DLQ entries by entry id (`{ms}-{seq}`, as returned by list_dlq's `id`) or by `requestId`. `requestId` is the durable handle — stable across a retry, unlike `id`, which changes every time an entry is replayed and later dead-letters again — and returns every fanned-out target's entry for that inbound request (one request can fan out to N targets, and several may dead-letter); `id` returns at mos
retry_dlq_entryRe-enqueue a DLQ entry to the main relay stream and delete it from the DLQ. A replay is billed like any relay — it charges the entry's original credit cost to the project before re-sending; if the balance is too low the entry stays in the DLQ and this returns an insufficient-credits error (top up and retry). Succeeds if the entry exists, errors if it's already gone (retried or discarded). Requires
discard_dlq_entrycan modifyPermanently delete a DLQ entry without retrying it: the delivery is never re-sent and the entry cannot be restored. Use retry_dlq_entry instead when the delivery should still reach its target. The inbound request stays answerable through get_request for the project's requestLogRetentionDays window. Errors if the entry is already gone (retried, discarded, purged or evicted). Requires edit access.
list_subscription_plansread-onlyList the available subscription plans (Free, Pro, Scale) with monthly EUR price, credit allowance, per-project RPM cap, and how many active projects the plan includes. Read-only.
list_credit_packsread-onlyList the prepaid credit packs available to purchase, returned as a bare array of `{credits, eurCents, usdCents, plnGrosze, welcomeEligible}` objects — not wrapped in an envelope, and not paginated (the whole catalog every time). Read-only; identify a pack to start_topup by its `eurCents`.
list_addonsread-onlyList the optional add-on subscriptions (RPM upgrades, extra team seats, extended log retention, extra file-delivery storage in +5 GB blocks, …). Each entry is monthly, renews every 30 days until cancelled, and carries `scope` (per_account or per_project). Read-only.
get_subscriptionread-onlyGet the project owner's current subscription: plan, active flag, period start/end, cancel-at-period-end flag, external Paddle subscription id, and whether a payment method is on file at the provider. Owner-only; requires a token minted with the billing scope.
preview_plan_changeread-onlyPreview what would happen if change_plan were called with this plan: the immediate-charge amount (cents), the next-invoice amount, the effective date, and the kind (`applied` for in-cycle upgrade, `scheduled` for at-period-end downgrade / cancel). Owner-only; requires a token minted with the billing scope.
start_subscriptionBegin a new Pro or Scale subscription for the project owner. Returns `{checkoutUrl, plan}` — surface the URL to the human for approval. Errors if the owner is already on an active paid plan (use change_plan instead). Owner-only; requires a token minted with the billing scope.
change_plancan modifySwitch the project owner between Pro and Scale, or cancel the subscription (effectively dropping to Free at period end). Returns `{kind, newPlan, effectiveAt, applied}` — `applied=true` means Paddle was charged immediately, otherwise the change is queued for the current period end. Use preview_plan_change first to see the dollar impact. Owner-only; requires a token minted with the billing scope.
cancel_subscriptioncan modifySchedule the project owner's subscription for cancellation at the end of the current billing period. Returns `{kind: 'scheduled', newPlan: 'free', effectiveAt}`. Add-ons keep renewing while the plan is active; after it ends they stop renewing and each already-paid add-on cycle stays active until its own end date (use unsubscribe_addon to stop one sooner). Owner-only; requires a token minted with t
set_overage_billingcan modifyTurn the project owner's overage billing on or off, for the whole account. Returns `{overageBilling}`, the setting get_billing reads back. Owner-only; requires a token minted with the billing scope.
start_topupBegin a credit-pack top-up for the project owner. Pass the pack`s `eurCents` (from list_credit_packs). Returns `{checkoutUrl, credits, eurCents, directCharge}` or `{transactionId, credits, eurCents, directCharge:true}` on subsequent MCP purchases. Owner-only; requires a token minted with the billing scope.
subscribe_addonSubscribe the project owner to a monthly add-on (RPM upgrade, team seat, extended log retention, extra file storage in +5 GB blocks, …). Charges the saved payment method immediately, then auto-renews every 30 days until `unsubscribe_addon` — or until the subscription ends, after which the already-paid cycle stays active to its own end date and stops without further charges. Requires an active Pro/
unsubscribe_addoncan modifyCancel an active add-on subscription. Stops future charges; the current cycle stays active until expiry. RPM upgrades are cancelled for THIS project. Returns `{addonKey, project, cancelledAt}`. Owner-only; requires a token minted with the billing scope.
resume_addonResume (un-cancel) a pending-cancel add-on whose paid-through cycle has not yet ended. Clears the cancellation so the add-on renews normally at the next cycle boundary. No new charge — the cycle was already paid. Stackable add-ons cannot be resumed; buy a new unit instead. Returns {addonKey, project, nextRenewalAt, resumed}. Owner-only; requires a token minted with the billing scope.
downgrade_addoncan modifySchedule a tier downgrade for a non-stackable add-on (RPM tier). The current tier stays active until `nextRenewalAt`; at that point the lower tier is charged and activated. No mid-cycle charge. Use list_addons to find available keys. Returns {addonKey, targetAddonKey, project, effectiveAt}. Owner-only; requires a token minted with the billing scope.
list_active_addonsread-onlyList the project owner`s currently-active add-ons. Returns `[{addonKey, project, monthlyEurCents, activatedAt, nextRenewalAt, consecutiveFailures}, ...]`. RPM upgrades are per-project; `project` is the slug they apply to (null for account-wide add-ons). Owner-only; requires a token minted with the billing scope.
list_audit_eventsread-onlyList project audit-log entries, newest first. Captures who changed what — lines, endpoints, targets, API keys. Outbound-target `auth.token` / `auth.password` are redacted in the diff per the same policy used for endpoint reads. Retention is the `auditRetentionDays` advertised on get_project (default 365 days); rows older than that are purged by the cleanup job. Returns `{total, limit, offset, rete
list_projectsread-onlyList the projects on your account (archived included), each with `id`, `slug`, `name`, `apiBaseUrl` and `archived`, plus `accountEmail` — the account this token authenticates as. Requires an ACCOUNT-scoped management token (one minted with no project) and the `read` scope. A PROJECT-scoped management token cannot call this: use it on its own project's tools instead.
create_projectCreate a new project on your account so an agent can bootstrap from a fresh account. The slug is derived from the name and validated server-side (format, reserved words, uniqueness). Each plan includes a fixed number of active projects (free tiers one; paid plans more: see list_subscription_plans); at the limit this errors. If an existing project can host this integration, skip create_project and
rename_projectcan modifyRename a project you own (the display name only: the slug, which identifies the project in URLs and API paths, never changes). Requires an ACCOUNT-scoped management token and the `config` scope. 404-equivalent error when you do not own the slug.
archive_projectcan modifyArchive a project you own. Reversible: the project + slug persist and credits are kept, but the data plane drops the tenant (callers get 404). Idempotent. Fails with an error if the project has archive protection enabled. That is a support-only unlock, not something this call can override. Requires an ACCOUNT-scoped management token and the `config` scope.
protect_projectTurn on archive protection for a project you own: archive_project (on every surface: panel, REST, and this tool) then refuses until it is lifted. Idempotent. Permanent from here: there is no tool or API call to turn it back off; only contacting support can. Requires an ACCOUNT-scoped management token and the `config` scope.
unarchive_projectRestore an archived project you own to the data plane. Restoring consumes one of your plan's active-project slots, so at the limit this returns an error telling you to archive another project or upgrade. Idempotent for an already-active project. Requires an ACCOUNT-scoped management token and the `config` scope.
create_project_tokenMint a PROJECT-scoped management token (`er_mcp_`) for MCP and REST; it cannot authenticate relay traffic. Use it after create_project to configure a fresh project, or for any project you already own. Attenuated by design: the scopes must be a subset of THIS management token's own grant (`read` is always included), expiry is mandatory (1–90 days, default 30, never "never"), and the minted manageme
list_project_tokensread-onlyList the management tokens scoped to a project you own (any holder): id, label, scopes, createdAt, lastUsedAt, expiresAt, revokedAt: never the secret (only its hash is stored). Requires an ACCOUNT-scoped management token and the `read` scope.
revoke_project_tokencan modifyRevoke a PROJECT-scoped management token on a project you own, any holder's, not just your own (the account-owner kill switch for a leaked or retired integration's management token). Soft-revoke: the row stays for traceability and the management token is rejected immediately. Idempotent. Requires an ACCOUNT-scoped management token and the `config` scope.
Alternatives in Developer Tools
Compare →
Context7A
Up-to-date docs for AI agents
OMOmnaraA
Deploy durable AI agents with Omnara, an open-source managed-agent platform.
AAAirbyte AgentsOfficialA
Let AI agents query data and act across all your business apps via MCP.
ARApple RagA
Apple developer docs and WWDC transcripts with RAG retrieval and Jev relevance ranking.
NENetdataA
Real-time infrastructure monitoring with metrics, logs, alerts, and ML-based anomaly detection.
VirusTotalOfficialA
Official VirusTotal MCP server: threat reports, file and URL submissions, and analysis recovery.