wishlist-mcp
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., "@wishlist-mcpWhat's on my sister's wishlist for her birthday?"
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.
wishlist-mcp
The hosted MCP server for wishlist.fit. It lets Claude, ChatGPT, or Codex read and update your wishlist on your behalf.
https://mcp.wishlist.fit/mcpConnect it from app.wishlist.fit/connect, which has per-client setup steps. You need a wishlist account first; this server never creates one.
docs/connecting.md has the longer version: exact commands for each client, what has actually been verified against production, and the two Codex flags that waste an afternoon if you get them wrong.
What it is
A pure OAuth 2.1 resource server in front of the wishlist REST API. It holds no database and no business rules. Visibility, rate limits, and invite handling all live in the wishlist API, and this server inherits them by calling that API with the user's own access token.
That split is deliberate. The alternative, a second service with its own copy of the rules, is how two surfaces quietly start disagreeing about who may see what.
Claude / ChatGPT / Codex
│ MCP over HTTP, bearer token
▼
this server ──── verifies the token (issuer + audience)
│
│ the same token, forwarded
▼
api.wishlist.fit ──── applies visibility, rate limits, invite rules
│
▼
PostgresWorkOS AuthKit is the authorization server. This server issues nothing and shows no consent screen; it only verifies what AuthKit signed.
Related MCP server: Personal Toolkit MCP Server
The tools
Twenty-nine, one per thing you can already do by hand in the web app. Parity is the rule: no agent-only privileges, and nothing the app itself cannot do.
Tool | Kind |
| read |
| read |
| read |
| read |
| read |
| read |
| read |
| read |
| read |
| read |
| read |
| read |
| read |
| read |
| write |
| write |
| write |
| write |
| write |
| write |
| write |
| write |
| write |
| write |
| write |
| write |
| destructive |
| destructive |
| destructive |
wishlist_get_gift_guide returns a person's profile and wishlist together. It is the
reason anyone connects this server, and without it the gift-giver journey costs three
round trips. It carries the fields that actually rule a gift in or out: dietary rules
and allergies, interests, price comfort, what they already own, and their birthday
without the year.
wishlist_upcoming_occasions answers "whose birthday is next" in one call, and pairs
with the gift guide: it hands back a username and a date, and the guide turns that into
something to buy. Birthdays in it are only people whose circle the caller is in, and
only those who filled one in, so an absent birthday is not evidence that
someone has none.
The three destructive tools carry destructiveHint: true, so clients that confirm
irreversible actions will ask first. Sending an invite emails a real person and cannot be
unsent. Accepting a request or an invite is reciprocal: the other person gains access to
your circle-only fields as well as you gaining access to theirs. Removing a member cuts
both ways at once, and getting back in needs a fresh request they have to accept.
Nudges
A nudge is a short question between two people already in the same circle: is your wishlist still current, do you still want this item, are your sizes still right, could you add a few ideas.
It is anonymous by default. wishlist_send_nudge takes signed, which defaults to
false; leave it there unless the user asks to be named, because asking whether someone
still wants an item, under your user's name, tells that person who is buying it. On the
receiving side an incoming nudge with a null username was sent anonymously: report it
as "someone in your circle" and do not try to work out who from wishlist_list_circle.
The server withheld the name deliberately, and naming a guess is worse than naming nobody.
The wording is not the caller's. wishlist_list_nudge_prompts
serves the catalogue, wishlist_send_nudge takes a prompt key, and nothing an agent
writes is delivered to the recipient. That is the point: with no free-text field there
is nothing to moderate and nothing a model can be argued into sending on someone's
behalf.
Sending one emails a real person, so confirm the question and the name first. The API refuses a nudge to anyone outside your circle, to anyone who has nudges off, more than once a day per person, for a week after they dismiss one, and after ten in a day. Each refusal names which and when to try again; report it rather than retrying.
Answering still_current records that your wishlist or profile was confirmed today,
which everyone in your circle sees on wishlist_reviewed_at and profile_reviewed_at.
The answer is the user's to give: ask which option they want rather than inferring one.
Two ways into a circle
wishlist_create_invite takes an email address and wishlist_request_circle takes a
username, but the API decides which path an address takes, not the caller. An address
with no account behind it gets a tokenized email; one that already belongs to someone
gets an in-app request and no email at all. wishlist_create_invite reports which
happened in its outcome field, and a caller that assumes an email went out will tell
the user something untrue.
One connection, everything
There are no scopes. WorkOS cannot express custom ones, so a connection carries the whole tool surface. Protection on writes is behavioural rather than structural: the annotations above, the tool descriptions, and the rate limits the API applies.
Running it locally
uv sync
cp .env.example .env # point it at a local API and the Local WorkOS environment
uv run python -m wishlist_mcp.mainuv run pytest
uv run ruff format --check . && uv run ruff check .Tests stub the wishlist API with respx. What they check is this server's own job:
verifying tokens, shaping requests, and turning API errors into sentences a model can act
on. The rules themselves belong to the API and are tested there.
Keeping parity
The wishlist API ships from another repo on another deploy, so an endpoint or a field can change there, reach the website, and leave this surface where it was. Nothing here detects that.
GiftProfile and MyProfile in schemas.py list readable fields explicitly, and
wishlist_update_my_profile lists writable ones as arguments, so a new column stops at
this boundary until someone moves it. The Dietary, GiftFormat, PriceComfort, and
InterestCategory literals are copies of the API's enums for the same reason.
Watch for a second failure mode that does not look like a gap: a tool reads one key out
of a response body, and an endpoint that starts answering with a second shape makes it
report the wrong thing confidently rather than fail. POST /invites did exactly that.
The workspace repo tracks the ledger in docs/mcp-parity.md.
Configuration
Every variable is prefixed WISHLIST_MCP_. See .env.example.
Variable | Purpose |
| The wishlist REST API to call |
| WorkOS AuthKit issuer. Empty means the server refuses to start |
| Canonical URI. Every token's audience must match it exactly |
| The server 404s on any other host |
| Seconds to wait on the API |
RESOURCE_URI must match the resource indicator configured in WorkOS character for
character, including the /mcp path. A mismatch is the most common reason a client
refuses to connect.
Things worth knowing before you change this
Each of these is a bug that reached production once.
get_http_headers()stripsauthorization. Ask for it explicitly. Without that, every tool call reports "no access token" whileinitializeandtools/liststill succeed, because those are answered before any tool body runs. A green handshake is not evidence the tools work.The MCP app is mounted at the root and owns its own path. Mounting it at
/mcpmakes Starlette redirect/mcpto/mcp/, and the address every client is handed has no trailing slash.X-Forwarded-Protois trusted. Cloud Run terminates TLS, so without it every generated URL claimshttp://. Combined with the redirect above, that once meant a client would have sent its bearer token in the clear.FastMCP's lifespan is chained into the app's. Mounting an ASGI app does not start its lifespan, and without it every tool call fails with "Task group is not initialized" while unit tests still pass.
TestClientfollows redirects by default. Passfollow_redirects=Falsewhen the point is that a path answers directly.Every path segment is encoded with
segment().httpxapplies RFC 3986 dot-segment removal before a request goes out, so an unencodedusernameorinvite_tokencontaining../is not a 404: it is a different endpoint, called with the user's own token and reported to the model under the name of the tool that was invoked. A?does the same by starting a query string. These arguments are chosen by a model that has read item names and bios other people wrote, so treat them as hostile.FastMCP matches
Acceptby substring. It answers406 Client must accept application/jsonto a client sending*/*, which already does, and to one sending no header at all, which under RFC 9110 also does.WildcardAcceptspells those out before FastMCP sees them. Plaincurlsends*/*, so this was the first thing anyone hit.
Deployment
Cloud Run, asia-southeast1, in the same project as the rest of wishlist. main deploys
on push. Infrastructure lives in wishlist-infrastructure.
Licence
MIT. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted MCP server connecting claude.ai, ChatGPT and other AI apps to your own computer
Hosted Amazon Seller and Vendor MCP server for Claude, ChatGPT, Cursor, Codex, Gemini, Copilot.
Cloud-hosted MCP server for durable AI memory
Hosted MCP server to manage a restaurant menu from AI agents - 39 tools over the DuckHub API.
Related MCP Servers
FlicenseAqualityDmaintenanceMCP server for the forme.gifts wishlist app that enables managing wishlists and gifts from Claude Code, Claude Desktop, Cursor, and other MCP clients.106 npm-- FlicenseNot gradedqualityDmaintenanceA local MCP server providing text utilities, math functions, and a persistent todo list, enabling task automation via natural language in Claude Desktop.-
- FlicenseNot gradedqualityDmaintenanceA custom MCP server that turns Claude into an agentic shopping assistant for searching products, comparing options, managing a cart, and checking out with built-in guardrails for safe autonomous commerce.-
- AlicenseNot gradedqualityCmaintenanceA production-ready MCP server that gives an LLM agent standalone-equivalent control over a Mineflayer Minecraft bot — movement, mining, crafting, inventory, combat, containers, chat, and much more — exposed as 110 strongly-typed tools across 23 groups, with full bot lifecycle management and dual (poll + push) event streaming.50 npm2MIT