Skip to main content
Glama
Plannr MCP Server

Plannr MCP Server

by dragosh29

Plannr MCP server

An MCP server that lets Claude, ChatGPT and other MCP clients work with a financial adviser's Plannr CRM: clients, households, reviews due, tasks, cases, plans and notes, and (when enabled) creating tasks and notes. It is built from Plannr's public documentation: the OpenAPI 3.0.3 spec "Plannr API Documentation" at apidocs.plannrcrm.com/source.json and the API guide at api-how-to.plannrcrm.com (authentication, required headers, pagination, rate limits).

Once it's connected, an adviser or administrator can ask things like:

  • "Whose annual review is due in the next month, and who is their adviser?"

  • "What's open for the Evans family: tasks, cases and plans?"

  • "Which of Sam Evans's pensions are under advice, and with which providers?"

  • "What did we last discuss with Sam? Show me the call notes since September."

  • With writes enabled: "Create a high-priority task to call Sam about drawdown on Friday, and log a meeting note on his record."

Tools

Tool

What it does

API calls

list_logins

The logins of the user behind the token: the account each acts as (UUID, name, type, role) and the firm. Shows which account the server acts for: PLANNR_ACCOUNT_UUID, or the one chosen from the same logins response by the rule below, or a note that none could be chosen.

GET /api/v1/logins

search_clients

Accounts (clients by default) by name, role, prospect flag, status, tags, assigned adviser or household, with sort.

GET /api/v1/account

list_reviews_due

Clients whose next review date falls in a window (today to +30 days by default), soonest first.

GET /api/v1/account with filter[next_review_date]=>=FROM,<=TO and sort=next_review_date

get_client

One account: adviser, administrator, paraplanner, owners, groups, tags, households, service level, review and agreement dates, custom field names.

GET /api/v1/account/{uuid}

list_households

Circles (households) with members by name, portal access and engagement rating.

GET /api/v1/circles

get_household

One circle with members, groups and last portal login.

GET /api/v1/circles/{uuid}

list_tasks

Tasks by client, assignee, related plan or case, priority, due-date range, open only, name, status, with sort; each with its author and who completed it.

GET /api/v1/task with include=author,completed_by

get_task

One task with its description, workflow and custom field names.

GET /api/v1/task/{uuid}

list_task_statuses

The firm's task statuses in board order (needed by create_task).

GET /api/v1/task-status

list_cases

Cases by client, household, employee, progress, review or completion dates, status or type, with participants by name. Sorting by value is not offered.

GET /api/v1/cases

get_case

One case with its linked plans and custom field names.

GET /api/v1/cases/{uuid}

list_plans

Plans by client, household, type, abstract type, status, provider, name or under-advice flag: type, provider, status, owners, review date. Sorting by value is not offered.

GET /api/v1/plans

get_plan

One plan with seller, sub-accounts by name and custom field names.

GET /api/v1/plans/{uuid}

list_client_notes

A client's notes, including notes on their plans, cases, tasks and risks, newest first, by type, date range, text or what they are attached to.

GET /api/v1/client/{client_uuid}/all-notes

create_task

Creates a task on a client, employee, case or plan. Only registered when writes are enabled.

POST /api/v1/task

add_note

Adds a call, note, meeting or email note to an account, household, case, plan or task, not visible to clients unless asked. Only registered when writes are enabled.

POST /api/v1/note

Not covered on purpose: everything else in an 875-operation API, including contact details and addresses endpoints, documents and files, fact finds, valuations, holdings, transactions, charges and fees, bank feeds, messages, webhooks, and every update and delete. Searching clients by email, phone number, National Insurance number or date of birth (all documented filters) is not offered either.

Related MCP server: Carebit MCP server

Setup

Requires Node 18 or later.

npm install
npm run build

You need a personal access token: in Plannr, go to Settings > Account Details and scroll to Personal Access Tokens. The guide recommends these for personal use and for agencies working on behalf of a Plannr customer. Plannr's OAuth2 authorization code flow (for apps used by many firms; client credentials are issued by emailing integrations@plannrcrm.com) is out of scope for this local version; see Going to production.

Most endpoints need the account to act for in an X-PLANNR-ACCOUNT-UUID header. Set PLANNR_ACCOUNT_UUID to the account.uuid of your employee login (the list_logins tool shows them). If it is not set, the server calls GET /api/v1/logins once and uses the response's preferred_account_uuid if there is one, otherwise your only employee login; if you have several employee logins it refuses and lists them so you can choose.

Claude Desktop: add this to claude_desktop_config.json:

{
  "mcpServers": {
    "plannr": {
      "command": "node",
      "args": ["/absolute/path/to/plannr-mcp/dist/index.js"],
      "env": { "PLANNR_ACCESS_TOKEN": "your-personal-access-token", "PLANNR_ACCOUNT_UUID": "your-employee-account-uuid" }
    }
  }
}

Claude Code:

claude mcp add plannr -e PLANNR_ACCESS_TOKEN=your-personal-access-token -e PLANNR_ACCOUNT_UUID=your-employee-account-uuid -- node /absolute/path/to/plannr-mcp/dist/index.js

Variable

Required

Meaning

PLANNR_ACCESS_TOKEN

yes

Your personal access token, sent as Authorization: Bearer …. A value pasted with its Bearer prefix is accepted.

PLANNR_ACCOUNT_UUID

no

The account to act for, sent as X-PLANNR-ACCOUNT-UUID. Resolved from GET /api/v1/logins when not set (see above).

PLANNR_ALLOW_WRITES

no

true to register create_task and add_note. Off by default.

PLANNR_BASE_URL

no

Defaults to https://api.plannrcrm.com. Used by the tests.

PLANNR_TOOL_BUDGET_S

no

Time budget per tool call in seconds (default 45; see Safety defaults). Lowered by the tests.

Every request carries Accept: application/json and Content-Type: application/json, which the guide lists as required, and every request except GET /api/v1/logins carries X-PLANNR-ACCOUNT-UUID.

Safety defaults

  • Read-only unless PLANNR_ALLOW_WRITES=true. Read tools carry the MCP readOnlyHint annotation; the two write tools are marked not read-only, not destructive and not idempotent. There is no update or delete tool.

  • Clients and everyone else are identified by UUID and name by default. Only with include_contact_details=true does a tool return email addresses (of accounts, logins, members, assignees, authors and participants), the primary email and phone number, custom field values (only their names are shown by default, because a firm can record income, health or anything else in them), third-party references (platform and provider references) and policy, proposal, linked-policy and sub-account policy numbers.

  • Never read from the records: money fields (plan values, valuations, case values, benefit amounts; see Status), bank details, National Insurance numbers, dates of birth and addresses. Every output is built field by field from a list of known fields, so a field of that kind is not copied even if a response carries it (the test suite serves records with date_of_birth, ni_number, bank_accounts, an address, an income and money objects and checks none of them comes back, with or without include_contact_details). The endpoints that hold contact details, addresses, documents and bank data are never called (the suite asserts the exact set of endpoints used). Sorting by value, which the API documents for cases and plans, is not offered, because the order would reveal the relative values the server otherwise withholds. No file is ever downloaded.

  • Free text (client, household, task, case, plan and sub-account names; tag, group, status, type, provider, service level, workflow and custom field names; firm names; task descriptions; note contents; the error messages Plannr returns) is redacted by pattern. Always, with or without include_contact_details: a sort code with an account number, a sort code or account number introduced as such ("sort code", "acct", "account no."), a GB IBAN ([bank details redacted]), a 13 to 19 digit number that passes the Luhn check ([card number redacted]), a National Insurance number in the QQ 12 34 56 C shape ([NI number redacted]) and a date written after "DOB", "born", "date of birth" or "birthday" ([date of birth redacted]). By default, and not with include_contact_details: email addresses ([email redacted]), phone-number-like sequences ([phone redacted]) and UK postcodes ([postcode redacted]). Custom field values, returned only on request, go through the always-on part.

  • These are heuristics. The phone match covers international numbers written with + or 00 (including +44 (0)7700 …), UK numbers with a bracketed area code, and UK-style 0… numbers of 9 to 11 digits with spaces, dots or hyphens between groups; other digit strings starting with 0 are redacted too, and so is any other 13 to 19 digit string that happens to pass the Luhn check (about one in ten), while UUIDs and timestamps are left alone. A bare date of birth, a bare six- or eight-digit number, a street address, an amount such as "salary £85,000" and a health detail are not caught and are returned as written, as is the rest of a note, since calling the notes tool is itself the request for notes. HTML tags in notes (such as the <span> of an @mention) are stripped.

  • IDs are checked before any call: every ID must be a UUID. Dates must be real calendar dates in YYYY-MM-DD form, a review window must not end before it starts, and a tag, status, provider or plan name may not contain a comma (the API takes these as comma-separated lists).

  • Rate limits: Plannr documents 500 requests per minute per user, an X-Ratelimit-Remaining header on every response and a 429 when the limit is exceeded, with the advice to back off; it does not mention Retry-After. Requests are spaced 150 ms apart (at most 400 a minute). A 429 is retried at most twice, waiting for Retry-After when present (in seconds or as an HTTP-date) and 2 s then 4 s when absent. Each wait is capped at 10 seconds; if Plannr asks for a longer wait the call gives up at once and says how long. Because one tool call can make several requests (the account lookup, then up to 10 pages of a list), every tool call also has a 45 s time budget (PLANNR_TOOL_BUDGET_S), under the MCP SDK's default 60 s request timeout: a retry wait that would end past it is not started and no further page is requested, the call fails at once saying so, and nothing is sent to Plannr after the client has given up. The server does not read X-Ratelimit-Remaining.

  • 502, 503 and 504 are retried the same way for GET only; when all three attempts fail the error says the service may be unavailable, without the gateway's page. POST /api/v1/task and POST /api/v1/note are never retried after a gateway error, because the task or note may already exist; the error says to check list_tasks or list_client_notes first.

  • A 200 whose body is not JSON (a proxy or login page in the way) is reported as an error naming PLANNR_BASE_URL, described by content type and size without quoting it. From error responses only Plannr's documented message and errors fields are passed on, redacted as above, and the access token is scrubbed from every message.

  • Pagination follows the links.next URL Plannr returns. The server never builds a page number itself, re-adds any of its own filters the link leaves out, and refuses to follow a link to another host or another endpoint.

  • A rejected token (401) produces a message that says which variable to fix and where tokens are created; a 403 names the account in use and points to PLANNR_ACCOUNT_UUID and list_logins; a 422 is passed on with Plannr's field errors.

Tests

npm test

The test suite:

  1. Validates every fixture record against the component schemas in Plannr's published OpenAPI spec (LoginResource, AccountResource, CircleResource, TaskStatusResource, TaskResource, IndexTaskResource, CasesResource, Plans_PlanResource, NoteResource) with Ajv and ajv-formats. Because the schemas set no additionalProperties: false and mark almost nothing as required, every fixture is also walked key by key (following $ref, allOf and items) and a key the schema does not declare fails the check. Negative controls check that a schema still rejects wrong types and that the walk reports undeclared keys at any depth. The spec is downloaded from apidocs.plannrcrm.com/source.json to spec.json on the first run.

  2. Starts a local mock of the API that serves those fixtures with the documented filters (every filter the server sends is applied, except filter[is_prospect] and filter[status] on /api/v1/account, since AccountResource declares neither a prospect flag nor a status; their pass-through is still asserted), the include-only relationships of the task index (author and completed_by only when included), per_page and the Links/Meta envelope the guide describes (on /api/v1/task the next link carries only the page number, elsewhere it keeps the query), Bearer auth with the spec's 401 body, a 403 for a missing or unknown X-PLANNR-ACCOUNT-UUID, 404s, 422s in the Plannr_ValidationError shape, an X-Ratelimit-Remaining header, a one-off 429 with Retry-After on GET /api/v1/task-status, and injected failures or replacement records on any endpoint (including a 429 on every other request, so that each page of a list is refused once). The mock's list, detail, created-task and error responses are validated against the response schemas the spec gives for each operation.

  3. Starts the built server and drives it over stdio with the official MCP client: 31 checks covering tools/list and annotations, every read tool, pagination across two pages to the point where links.next is null (and a max_results cut reported as incomplete), a next link without the filters (they are re-added) and requests on one call spaced at least 140 ms apart, every documented filter each tool offers passed through exactly, the reviews-due operator filter, redaction of emails, phone numbers, custom field values, references and policy numbers by default and their return on request, contact details typed into client, household, tag, group, service level and custom field names and descriptions redacted, a fact-find note whose NI number, date of birth, bank details and card number are redacted in both modes and its postcode by default only, planted records served in place of every read (and of the created task and note, and of a 422) with those details and contact details in every free-text field and undeclared date_of_birth, ni_number, bank_accounts, address, income and money fields, none of which comes back, HTML stripped from notes, no money fields in plan and case output, a detail answer and a created task or note wrapped in {"data": …} unwrapped, the POST /api/v1/task and POST /api/v1/note bodies validated against the spec's request schemas (StoreTaskRequest, StoreNoteRequest), a 422 passed on with field errors, an empty 204 on a note handled, writes absent with PLANNR_ALLOW_WRITES unset and set to false, bad UUIDs, dates, reversed review windows and comma-containing names rejected before any request, the 404 message, the 429 retry waiting for Retry-After in the seconds and HTTP-date forms and 2 s then 4 s without it, giving up after three attempts and at once above the cap, a 429 on POST /api/v1/note retried once, a 429 on the first GET /api/v1/logins with two calls at once resolving the account once, the per-call time budget (lowered to 3 s) stopping a list whose pages each get a 429 with a clean error and no later request, a 502 retried for GET, a GET failing three times with 503 reported without the HTML, a 502 on POST /api/v1/task and a 503 on POST /api/v1/note not retried, a non-JSON 200 reported as an error, next links to another host or another endpoint refused, an error message passed on with its email, phone number and the token redacted, the account resolved from /api/v1/logins (the only employee login, preferred_account_uuid, and a refusal listing the choices when ambiguous), list_logins showing the account it acts for with PLANNR_ACCOUNT_UUID unset (or that none was chosen), the 401 message for a wrong token (pasted with a Bearer prefix, not doubled), the 403 message for a wrong account, and that every request used the Bearer token, the required headers, a documented method and path and only query parameters that operation documents (apart from the page number taken from Plannr's next link), with the exact set of 15 endpoints used.

Status

This is a working prototype. It has not yet been run against the live API, because it was built without a Plannr account (no public trial or sandbox was found; personal access tokens come from customers' own accounts). Everything below is taken from the published documentation and should be confirmed on a real account:

  • Pagination. The spec documents only per_page (default 15, max 500) and declares only data in its list responses; the guide describes a Links block with four page links and a Meta block (per page, total, current and last page) in a screenshot. The key names used here (links.first/last/prev/next, meta.current_page/last_page/per_page/total) are the Laravel convention that description matches. Whether links.next keeps the filters is not documented; both cases are handled. Whether per_page=100 is accepted everywhere this server uses it.

  • Get-by-UUID and create responses. The spec types them as the resource itself; if the live API wraps them in {"data": …} they are unwrapped.

  • POST /api/v1/note: the spec documents no success response (only 401 and 422). The mock answers 201 with a NoteResource; the tool also accepts an empty answer.

  • The account header: what status a missing or wrong X-PLANNR-ACCOUNT-UUID gets (the mock answers 403), whether GET /api/v1/account/{uuid} and the notes endpoints need it (the spec does not declare it there; the guide says every endpoint but the logins list does, so it is always sent), and where preferred_account_uuid sits in the GET /api/v1/logins response (the guide names it but it is not in the spec; the server looks at the top level and on each login).

  • Filter encoding and semantics: booleans are sent as true/false; the operator filter filter[next_review_date]=>=2026-10-01,<=2026-10-31 is sent URL-encoded; whether filter[client_uuid] on tasks includes tasks on the client's cases and plans (the mock matches tasks on the client only); whether filter[status] on accounts matches what the UI calls a client status (the AccountResource schema has no status field); which relationships the index routes return without an include (the mock returns only those included).

  • Relationship shapes: the spec types a case's participants and plans as single objects and every *_Summary relationship without property types; the fixtures follow the spec, and the server reads either an object or an array.

  • Money: the spec's money objects (value, total_benefit_amount, valuations) describe example and description as properties of amount, formatted and currency, so no real money value can pass the schema. This version therefore returns no money fields at all (amounts typed into free text are returned as written, see Safety defaults); valuations are the first thing to add once the live shape is known.

  • Dates: task due dates follow the spec's example format 2021-01-01 00:00:00; review dates are ISO 8601 with an offset. The server passes both through as given.

  • The wording of Plannr's 404 and 422 messages for these endpoints (the mock uses the forms the spec documents for other endpoints) and of a 429 body (not documented; the mock uses Laravel's "Too Many Attempts.").

  • Visibility: the spec says task lists are constrained by the user's role; the server returns whatever the token's login may see.

  • How the API reacts to requests at the spaced rate here; the documented limit is 500 a minute per user.

Going to production

This version runs locally over stdio with the user's own personal access token. For advisers to connect from claude.ai or ChatGPT without handling tokens, the next step is a remote server (Streamable HTTP) that uses the OAuth2 authorization code flow Plannr already offers, hosted by Plannr, so each user's own login and permissions apply, and then a listing in the Claude and ChatGPT connector directories. Valuations and other amounts can follow once the money shape is confirmed on a live account, and more write tools (completing tasks, updating review dates) once they can be tested on a test firm.

Licence

MIT. Built by Alexandru Dragoș (alexandru.dragos96@gmail.com) with an AI agent (Claude) working under his direction.

Available Tools

14 tools
get_caseGet caseA
Read-onlyIdempotent

One case by UUID: type, status, review and completion dates, participants by name, linked plans by name and type, custom field names. Uses GET /api/v1/cases/{uuid}.

Inputs: case_uuid (required), include_contact_details.

get_clientGet clientA
Read-onlyIdempotent

One account (usually a client) by UUID: name, type and role, adviser, administrator and paraplanner, owners, groups, tags, households, service level, review, agreement and first-contact dates, and custom field names. Email, phone, custom field values and third-party references only with include_contact_details. Uses GET /api/v1/account/{uuid}.

Inputs: client_uuid (required), include_contact_details.

get_householdGet householdA
Read-onlyIdempotent

One circle (household) by UUID with its members by name, groups, portal access, engagement rating and last portal login. Uses GET /api/v1/circles/{uuid}.

Inputs: household_uuid (required), include_contact_details.

get_planGet planA
Read-onlyIdempotent

One plan by UUID: type, provider, status, owners, seller, sub-accounts by name, review and valuation dates, custom field names. Policy numbers, linked policy numbers, third-party references and custom field values only with include_contact_details; money amounts never. Uses GET /api/v1/plans/{uuid}.

Inputs: plan_uuid (required), include_contact_details.

get_taskGet taskA
Read-onlyIdempotent

One task by UUID, with its description, status, what it relates to, assignees, workflow and custom field names. Uses GET /api/v1/task/{uuid}.

Inputs: task_uuid (required), include_contact_details.

list_casesList casesA
Read-onlyIdempotent

Cases (pieces of advice work such as a pension transfer or a mortgage) with type, status, review date, completion and participants by name, filtered as GET /api/v1/cases documents: by client, household, employee, progress, review or completion date range, status or type. Case values are not returned.

Inputs: sort, statuses, completed, type_uuids, +10 more.

list_client_notesClient notesA
Read-onlyIdempotent

Notes for one client, including notes on their plans, cases, tasks and risks, newest first by default: type (call, note, meeting, email), what the note is about, author, contents. Filter by type, date range, text, or what the note is attached to. Emails, phone numbers and postcodes in the contents are redacted unless include_contact_details; bank details, card and National Insurance numbers and dates of birth always. Uses GET /api/v1/client/{client_uuid}/all-notes.

Inputs: client_uuid (required), to, from, sort, +5 more.

list_householdsList householdsA
Read-onlyIdempotent

Circles (households and other groups of client accounts) with their members by name, portal access and engagement rating. Filter by name or member account. Uses GET /api/v1/circles with include=accounts.

Inputs: name, sort, max_results, member_uuids, +1 more.

list_loginsList loginsA
Read-onlyIdempotent

The logins of the user behind the access token: for each, the account it acts as (UUID, name, type such as employee or client, role) and the firm, plus the account this server acts for (PLANNR_ACCOUNT_UUID, or the one chosen from these logins). Use it to find the account UUID for PLANNR_ACCOUNT_UUID. Uses GET /api/v1/logins.

Inputs: include_contact_details.

list_plansList plansA
Read-onlyIdempotent

Plans (pensions, investments, protection, mortgages and other products) with type, provider, status, owners by name, review date and whether they are under advice, filtered as GET /api/v1/plans documents. Policy numbers only with include_contact_details; valuations and other money amounts are not returned.

Inputs: name, sort, types, provider, +7 more.

list_reviews_dueReviews dueA
Read-onlyIdempotent

Clients whose next review date falls between two dates (today to 30 days ahead by default), soonest first, with their adviser and previous review date. Uses GET /api/v1/account with filter[type]=client, the documented operator form filter[next_review_date]=>=FROM,<=TO and sort=next_review_date.

Inputs: to, from, max_results, assigned_adviser_uuids, +1 more.

list_tasksList tasksA
Read-onlyIdempotent

Tasks on the firm, filtered as GET /api/v1/task documents: by client, assignee, related plan or case, priority, due date range, open only, name or status. Each task has its status, priority, due date, what it relates to, who it is assigned to, its author and who completed it (include=author,completed_by). What a user sees depends on their Plannr role.

Inputs: name, sort, due_after, open_only, +9 more.

list_task_statusesList task statusesA
Read-onlyIdempotent

The task statuses defined in the firm's settings, in board order, with which ones count as completed, archived or not started. create_task needs one of these UUIDs. Uses GET /api/v1/task-status with sort=position.

Inputs: no named parameters.

search_clientsSearch clientsA
Read-onlyIdempotent

Search the firm's accounts (clients by default) by name, role, prospect flag, status, tags, assigned adviser or household, with the documented filters of GET /api/v1/account. Each result has the UUID, name, type and role, assigned adviser, review dates, tags and households. Email and phone only with include_contact_details.

Inputs: name, sort, tags, type, +7 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 14 tools

    Disambiguation4/5

    Most tools are clearly distinct list/get pairs for clients, households, tasks, cases, and plans. There is mild overlap between search_clients and list_reviews_due (both surface clients from the same account endpoint), but descriptions describe distinct purposes (general search vs review-date filtering) adequately.

    Naming Consistency4/5

    Strong underlying list_*/get_* verb_noun pattern across clients, households, tasks, cases, and plans. Minor deviations: search_clients uses a different verb than the otherwise dominant list_* convention, and list_reviews_due/list_client_notes are more descriptive than schematic, but overall it is readable and predictable.

    Tool Count5/5

    14 tools is well within the sweet spot and each maps to a distinct resource or lookup. No redundant or trivial tools pad the set.

    Completeness3/5

    Read coverage is thorough (list+get for nearly every entity plus lookups like task statuses and logins), but there are no write/lifecycle operations such as update_task, create_case, or delete_plan. The mention that 'create_task needs one of these UUIDs' implies mutation tools exist elsewhere but are absent here, leaving a notable gap for a CRM-style server.

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      D
      maintenance
      Enables querying and managing a CRM database through natural language conversations with Claude Desktop.
      -
    • A
      license
      B
      quality
      C
      maintenance
      Enables Claude, ChatGPT and other MCP clients to read practice-management data including organization, clinicians, diaries, availability, bookings, patients, invoices, payments, staff tasks, services, and locations, and optionally create staff tasks, create bookings, and cancel bookings.
      15
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables 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
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables 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