Engaging Networks MCP server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Engaging Networks MCP serverIs jane@example.com still opted in to email and a member?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Engaging Networks MCP server
An MCP server that lets Claude, ChatGPT and other MCP clients work with an Engaging Networks (ENS) account: campaign pages, supporters, their transaction history and recurring gifts, supporter fields and questions, and marketing automation statistics, and (when enabled) updating a supporter's opt-ins. It is built from Engaging Networks' public developer documentation: the OpenAPI 3.1 document "Engaging Networks Services REST API" v6.5.0 at developer.engagingnetworks.net/api/rest/engagingnetworks.app.json, and the knowledge-base pages on the REST services.
Once it's connected, someone at the organisation can ask things like:
"Which donation pages are live right now, and which petitions have we closed?"
"Is otto.nv@example.com still opted in to email? Is he a member?"
"What has supporter 212200 done with us: gifts, petitions, events? Does he have a monthly gift running?"
"Who signed up in the last week?" / "Find supporters called Bob in the UK or US."
"How is the welcome journey doing this year: open rate, donations, unsubscribes?"
With writes enabled: "Otto asked by phone to stop texts: opt him out of SMS."
Tools
Tool | What it does | API calls |
| Campaign pages of one type ( |
|
| One page: type, subtype, status, locale, base URL, template, tracking parameters, attributes. |
|
| One supporter by email address or supporter ID: ID, suppression flag, name, and optionally question and opt-in answers and memberships. Every other field only on request (see Safety defaults). The supporter field list is fetched once per session to recognise name fields. |
|
| The API's supporter queries: |
|
| A supporter's history (donations, event tickets, petition signatures, emails to targets, data captures, email broadcasts, peer-to-peer) with page names, dates and statuses, plus their recurring schedules (amount, currency, frequency, status, next payment date). The history list has no amounts; only the recurring schedules do. |
|
| The account's supporter fields: name, tag and standard property. |
|
| Questions and opt-ins with their type ( |
|
| How one question is presented per locale: label, field type, answer options or range. At most 50 locales and |
|
| Marketing automations with status, filtered by dashboard folder (the API defaults to the Home folder) and part of the name. At most |
|
| One automation and its statistics (journey starts, open and click rates, actions, donations, objective reached, unsubscribes, SMS delivery rate, jumps), optionally for a range of months. |
|
| Sets named opt-ins to |
|
Authentication uses POST /authenticate (see Setup).
Not covered on purpose: page processing (donations, actions and card payments through /page/{id}/process), survey responses (the response schema and the example for GET /page/{id}/survey disagree on its shape), single-transaction detail (GET /supporter/{supporterId}/transactions/{transactionId} takes the payment gateway's transaction ID, which the history list does not return, and answers with card digits and expiry), page components, import formats, creating, updating or deleting supporters beyond opt-ins, bulk suppression, origin sources, migrating or changing recurring gifts, export jobs and their downloads, adding supporters to automations in bulk, the audit log, and the token validation and retirement endpoints.
Related MCP server: NewZapp MCP server
Setup
Requires Node 18 or later.
npm install
npm run buildYou need the token of an API User. An administrator of your Engaging Networks account creates the API user in the dashboard, gives it permissions (for example view permission on supporter data), and whitelists the IP address of the machine this server runs on. The server posts that token to /authenticate, receives a session token, and sends it in the ens-auth-token header of every other call. The session token is cached, renewed a minute before its documented expiry, and fetched again once if a call answers 401 "Invalid ens-auth-token".
Claude Desktop: add this to claude_desktop_config.json:
{
"mcpServers": {
"engagingnetworks": {
"command": "node",
"args": ["/absolute/path/to/engagingnetworks-mcp/dist/index.js"],
"env": { "ENGAGINGNETWORKS_API_TOKEN": "your-api-user-token", "ENGAGINGNETWORKS_REGION": "ca" }
}
}
}Claude Code:
claude mcp add engagingnetworks -e ENGAGINGNETWORKS_API_TOKEN=your-api-user-token -e ENGAGINGNETWORKS_REGION=ca -- node /absolute/path/to/engagingnetworks-mcp/dist/index.jsVariable | Required | Meaning |
| yes | The API User token. |
| no | The datacentre your account is on, from the spec's server list: |
| no | Overrides the region's base URL. Used by the tests. |
| no |
|
Safety defaults
Read-only unless
ENGAGINGNETWORKS_ALLOW_WRITES=true. Read tools carry the MCPreadOnlyHintannotation;update_supporter_opt_insis marked as a write that is not destructive and is idempotent. There is no tool that sends email, processes a page or a payment, or deletes anything.Supporter records come back with the account's own field names as keys. By default
find_supporterreturns the supporter ID, the suppression flag, the name fields (title, first, middle and last name, recognised through/supporter/fieldsby field name or tag), opt-in and question answers when asked for, and memberships when asked for. Every other field (email address, phone numbers, postal addresses, date of birth, appeal code and custom fields) is withheld and only its name is listed;include_contact_detailsreturns them as stored.Card and bank data are never returned, with or without
include_contact_details: fields whose standard property is a card holder name, bank account number, routing number, bank account type or password are dropped and only counted, and so is any field whose name or tag, split into words (_,-and.as separators, camelCase split, soccExpiryreads as "cc Expiry"), contains the word card or cards, credit card, cc followed by num, number, no, exp, expiry, expiration, cvv, cvc, holder or type, expiry or expiration, token, mandate, debit, CVV/CVC, bank, IBAN, BIC, SWIFT, sort code, routing, account number, no or type, PayPal, billing agreement, password, PAR, BIN, or last 4 / last four. This errs towards dropping: a custom "Membership Expiry" field is dropped too (the memberships list carries the term dates). A payment field named in some other way is not recognised by its name. A card number typed into any returned text (13 to 19 digits passing the Luhn check) is replaced by[card number redacted], also on request. Recurring schedules never include the payment gateway's transaction ID (which embeds the processor's customer reference), and the endpoint that answers with card digits and expiry (single-transaction detail) is never called.In free text (question answers of every type, supporter, peer-to-peer and member names, email-to-target targets, recurring-gift change reasons, page names and titles, and Engaging Networks' own error messages) email addresses (also URL-encoded,
name%40example.org) become[email redacted], phone-number-like sequences[phone redacted]and postcodes[postcode redacted]by default. Question answers are redacted whatever theirtypesays; the opt-in valuesY,N,PandDare unchanged by it. Page names and titles are always redacted (the page tools have no switch).query_supportersreturns email addresses only on request, andget_supporter_transactionsreturns a peer-to-peer fundraiser's email only on request. The free-text redaction is pattern matching, not a guarantee:phone numbers: international numbers written with
+or00; UK numbers with a bracketed area code and UK-style0…numbers of 9 to 11 digits (other digit strings starting with0are redacted too); North American numbers as(613) 555-0142,613-555-0142,613.555.0142,613 555 0142, optionally after1(1-800-555-0199). Ten digits written without separators are not recognised.postcodes, in capitals: UK (
EC1M 5PX), Canadian (K1A 0A2,K1A0A2), US ZIP+4 anywhere (20500-0003), and a five-digit ZIP only directly after a US state code (DC 20500,Washington, DC 20500; Idaho'sIDis left out, since "ID 12345" is usually an identifier). A lone five-digit number is left alone.street addresses and dates typed into free text (for example "24 Sussex Dr" or "born 04/12/1980") are not recognised.
The API user token and the session token are scrubbed from any text passed on: the spec's own 401 example for
/authenticateechoes the token it was given ("Invalid api key [...]").IDs are checked before any call is made: page, supporter, question, profile and automation IDs must be positive integers (the spec types them all as integers), months must be
YYYYMMwith the start not after the end, aprofilequery needsprofile_idand asearchquery needsfilter(as the spec says).update_supporter_opt_insfetches the account's questions first and refuses, without writing anything, a name that is not a question, a general question, a name given twice, or a double opt-in (CONF) question set toY, which would skip the supporter's own confirmation step. The body is{ "questions": { "<opt-in name>": "Y" | "N" } }, the shape of the spec's "Update the supporters contact preferences" example, and holds no other field.Lists the API returns whole are capped:
list_pages,list_supporter_questions,list_marketing_automationsand both lists ofget_supporter_transactionsreturn at mostmax_resultsentries (default 100, at most 1000) and say how many there are in total;get_supporter_questionreturns at most 50 locales andmax_optionsoptions per locale.query_supportersreturns at mostmax_resultssupporters and says whichstartto continue from; if the API returns more rows than were asked for, the continuation starts at the first row that was cut.Rate limits, as documented: 5,000 requests per hour per API User, and 200 page-processing requests per 5 minutes from one IP address (this server does no page processing); beyond these limits requests are blocked. The spec documents running out of the hourly allowance as a 401 with the message "... has exceeded its api limit.", not as a 429: that answer is reported as a used-up allowance, and no new session is requested for it. Requests are spaced 200 ms apart; the hourly allowance is not counted locally, because other integrations using the same API user share it. Each
query_supporterspage is one request of up to 100 rows.The spec documents no 429. One is still retried at most twice for any method, including the
PUT, on the assumption that a rate-limited request was not processed (see Status). The retry waits forRetry-After(whole or fractional seconds, or an HTTP-date; 2 s then 4 s when the header is absent). Each wait is capped at 10 seconds; if a longer wait is asked for the call gives up at once and says how long to wait.502, 503 and 504 are retried the same way for
GETand forPOST /authenticateonly; when all three attempts fail the error says the service may be unavailable, without the gateway's HTML. ThePUT /supporter/{supporterId}is never retried after a gateway error, because it may already have been applied; the error says to check withfind_supporterfirst.A 200 whose body is not JSON (a proxy or a login page in the way) is reported as an error naming the region and base URL settings, never as an empty list; the excerpt goes through the redaction first.
Tests
npm testThe test suite:
Validates every fixture record against the response schemas in the ENS OpenAPI document (pages and page details, supporter fields, questions and question details, supporter records by email and by ID, supporter query rows, transactions, recurring schedules, automations and automation stats). The spec is downloaded from
developer.engagingnetworks.nettospec.jsonon the first run. Three Ajv settings work around defects in the document:unicodeRegExp: false(the locale pattern^[a-z]{2,3}\-[A-Z]{2}$is invalid as a Unicode regular expression),validateSchema: false(some schemas giveexamplesas an object) andstrict: false(OpenAPI keywords). Two schema defects are handled explicitly and asserted: the supporter schemas'questions[].responsepattern rejectsY,Nand any punctuation, including the spec's own getSupporterByEmail example and the pattern's own example value, so fixtures are validated against a copy without that one pattern; and the pagesubTypeschema rejects the empty string the spec's page examples use, so the fixtures omitsubTypeinstead. The transaction list schema is ananyOfof objects with nothing required, so each transaction fixture is also checked for the keys of the spec's example of its type. Peer-to-peer payments (ppay,pacs,pacr) are mapped by the spec's discriminator totransactionP2Pdonation, but every transaction schema's owntypeenum leaves those values out, and that schema'scampaignIdandstatusareoneOfs that any ordinary value matches more than once; theppayfixture is validated against its properties with those two read asanyOf, and each defect is asserted. Negative controls check that the schemas reject an undocumented page type and recurring frequency.Starts a local mock of the API under
/ens/servicethat implementsPOST /authenticate(the API user token as the raw body or as a JSON-quoted string, since the spec can be read either way, answering 401 with the documented "Invalid api key [...]" body, messageId 10000000, for a wrong one),GETandDELETE /authenticate/{ens-auth-token}, theens-auth-tokencheck with the documented 401 "Invalid ens-auth-token", the documentedtypeandstatuspage filters, the five supporter query types withstart/rowspaging,daysBack,profileIdandfilter, thefolderIdandnameautomation filters, and the opt-in update. It answers the firstGET /mawith a 429. Its authentication, list, record, update and error responses are validated against the documented schemas; the 404 body (the spec documents no 404) is checked against the documented{ message }error shape.Starts the built server and drives it over stdio with the official MCP client: 31 checks covering tool annotations; the first call's
POST /authenticatewith the raw token body (asserted as the reading this client implements, not as conformance) and JSON content type, then reuse of the session token;list_pagespassingtypeandstatusexactly, with name and title redaction (UK and North American phone numbers, Canadian postcodes, ZIP codes) andmax_results;get_page;find_supporterby email and ID with names only by default, the renamed middle-name field recognised through/supporter/fields, the withheld field names,includeQuestionsandincludeMembershipssent only when asked, redaction of emails, UK phone numbers and postcodes in answers and names, and of North American phone numbers, Canadian postcodes and ZIP codes in a supporter's name and general answer; answers with an undocumented type (GEN, lower case, none) redacted too; every field returned withinclude_contact_detailsexcept the five card, bank, password and PayPal fields of the spec's example, five custom payment fields (a token,cc_num_last4, a card expiration date,ccExpiry, a direct debit mandate) and a Luhn-valid card number typed into a custom field; unknown emails (404, or 200 without a supporter) as "not found";query_supporterspaging at start 0, 100 and 200 up to the documented total of 253 and stopping, with requests at least 190 ms apart (the 200 ms spacing), continuing fromstart, a correct continuation when the API returns more rows than asked for, an echoed filter in an error message with its URL-encoded email and phone number redacted, and passingdaysBack,profileIdandfilterthrough exactly, with the profile and search requirements refused locally;get_supporter_transactionswith every documented transaction shape, seconds and milliseconds timestamps, recurring schedules without the gateway reference, the P2P email only on request,max_resultscapping the recurring schedules too, page names and a North American change reason redacted, and appaypayment keeping its site ID; the question tools, and the caps on questions, question options and locales, and automations; the automation tools after a 429 retry that waits forRetry-After, withfolderId,name,startMonthandendMonthpassed through and bad month ranges refused locally; the opt-in update's body validated against the documentedPUT /supporter/{supporterId}request schema and its local refusals; invalid IDs and out-of-rangemax_results/max_optionsrefused before any request, and the 404 message; the session token scrubbed from an error the API echoes it in; four concurrent calls on an expired session sharing onePOST /authenticate; a retired session replaced once and the call retried; a short-lived session renewed before it expires; the documented usage-limit 401 on a data call (no new session) and on/authenticate; a persistent 429 giving up after three attempts and aRetry-Afterabove the cap giving up at once; HTTP-date and fractionalRetry-After; a GET failing three times with 503, a 502 retried on a GET and onPOST /authenticateand never on thePUT; a non-JSON 200 and a 200 from/authenticatewithout a token; the write gate with the variable unset and set tofalse; a wrong API token giving an actionable message with the echoed token scrubbed; and that every request used a documented method and path, with the raw token only onPOST /authenticateand a mock-issuedens-auth-tokenon everything else.
The suite runs in about 35 seconds.
Status
This is a working prototype. It has not been run against the live API, because it was built without an Engaging Networks account (the company offers no trial or sandbox that we could find). Everything below is taken from the published spec and knowledge base and should be confirmed on a real account:
The body of
POST /authenticate. The spec types it as a JSONstringunderapplication/json; the knowledge base says to put "the token in the body". This server sends the token as the raw body, without JSON quotes. If the API expects a JSON-encoded string ("..."), that is a one-line change. The mock accepts both forms, so the tests do not settle this.The session lifetime:
expiresis documented in milliseconds (example 3,600,000, one hour), and an expired or retired session is assumed to answer 401 "Invalid ens-auth-token".Which datacentre a UK account is on. The default region is
ca(the first server in the spec, labelled "Canada / Europe").What
GET /supporter?email=answers for an address that is not on the account, and what any endpoint answers for an unknown ID: the spec documents only 200 and 401 (and a 204 for single-transaction detail). The server treats a 404, or a 200 withoutsupporterId, as "not found".Whether supporter record keys are the account's field names or its tags. The spec's examples use names such as "Email Address" and "First Name", which are both in its fields example; the server matches either.
The real values in
questions[].response(the spec's example isY, while its schema's pattern forbids it), and whether general answers are returned in full. Every answer goes through the redaction whatever its type, so this does not affect what is withheld.Peer-to-peer payment transactions (
ppay,pacs,pacr): the spec's discriminator maps them to a schema withsiteId, but thetypeenums leave them out; the server readssite_idfrom any transaction that has one.Whether a real page list carries
subType: ""for pages without a subtype, as the spec's examples do; the server treats an empty subtype as none.The
startparameter ofGET /supporter/query. The parameter's example is 0 and the server treats it as the 0-based index of the first row, but the response example shows"start": 1for a one-row result. If it is 1-based, the paging offset is one row out. Also to confirm: whatrowsabove 100 does, whether a page can hold more rows than asked for (handled, but not observed), which query types honourdaysBack, and the sort order of each query type.The
filtersyntax of search queries is passed on exactly as given; which field names it accepts (the example usesfirstNameandcountry) is not documented beyond that example.GET /supporter/questions/{id}: whether{id}is the question'sidor itsquestionId(the list returns both; the detail example usesid).GET /mawithoutfolderId: documented as defaulting to the Home folder, so automations in other folders needfolder_id. Whether thenamefilter is case-sensitive is not documented; the mock matches case-insensitively.createdDatein the transaction list: the spec's examples mix seconds (1519918699) and milliseconds (1424408400000); values below 10^11 are read as seconds.createdOn(for example "01/03/2018") is passed on as a string because the day/month order is not documented.The transaction history list carries no amounts; single-gift amounts are only in the per-transaction detail, which needs the gateway's transaction ID that the list does not return. Confirm whether the list on a real account includes more than the spec shows.
PUT /supporter/{supporterId}with only aquestionsobject: that it changes only those opt-ins, that questions are keyed by their dashboard name, and what it answers for an unknown name (the documented 400 "The following fields are not present in the account" belongs toPOST /supporter).A 429 on the
PUTis retried on the assumption that a rate-limited request was not processed; the spec documents no 429 at all.Which API user permissions each endpoint needs, and what a missing permission looks like (a 403 is assumed and reported as a permission problem; it is not documented).
The statistics' month range: whether
startMonthandendMonthare inclusive and what the defaults are.
find_supporter cannot search by name; query_supporters with type search and a filter on name fields is the documented way.
Going to production
This version runs locally over stdio, with the API user's own token, and the machine it runs on must be whitelisted for that API user. For organisations to connect from claude.ai or ChatGPT without handling tokens, the next step is a remote server (Streamable HTTP) behind OAuth, hosted by Engaging Networks, and then a listing in the Claude and ChatGPT connector directories.
Licence
MIT. Built by Alexandru Dragoș (alexandru.dragos96@gmail.com) with an AI agent (Claude) working under his direction.
This server cannot be deployed
Maintenance
Related MCP Connectors
Read campaigns, donations, profiles and supporters; record offline donations and upsert users.
The HubSpot MCP Server acts as a bridge that enables AI assistants and Large Language Models to securely interact with HubSpot CRM data through natural conversation, without requiring users to understand complex API structures. It provides read-only access to standard CRM objects (contacts, companies, deals, tickets, products, invoices, and more) and their associations, secured via OAuth 2.0, allowing AI agents to perform tasks like summarizing deals, fetching company updates, and looking up record changes.
Connect your ads, shop, analytics, social, CRM and finance platforms once, then let Claude, ChatGPT, Cursor or any MCP client read, join and explain your numbers. Public statistics from the World Bank, IMF, Eurostat, OECD, WHO and SEC filings come as context, searchable and chartable from the same tools. Read-only by design, every number carries its source.
Read and edit GA4, Search Console and Google Tag Manager from any MCP client. 29 tools.
Related MCP Servers
- AlicenseAqualityCmaintenanceLets Claude, ChatGPT and other MCP clients read a SmartSurvey account's surveys, survey designs, responses, exports and folders, and — when writes are enabled — open or close a survey or send an existing invitation to one named recipient. It runs read-only by default, redacting respondent contact details and phone-number-like text unless contact details are explicitly requested.7MIT
- AlicenseNot gradedqualityCmaintenanceEnables Claude, ChatGPT and other MCP clients to read NewZapp account details, campaign reports, open heatmaps, contact groups and contact counts, and to search contacts with personal data withheld by default; when writes are enabled, it can also create and update contacts.MIT
- AlicenseNot gradedqualityCmaintenanceEnables Claude, ChatGPT and other MCP clients to search a membership database's people and organisations, read a contact's summary and membership history, list events with ticket types and attendance lists, read invoices, and find and count segments. When writes are enabled, it also lets clients record who attended an event.MIT
- AlicenseAqualityCmaintenanceEnables MCP clients such as Claude and ChatGPT to read a campsite's supplier account data from the Pitchup.com API — campsites, pitch types, pitches, charge types, prices and stay rules, allocation, extras and arrivals and bookings. When writes are explicitly enabled, it also sets allocation, prices and charge types, with writes off and the sandbox environment used by default.14MIT