Look up your contacts in Cursor
A contact is one person across everything Launch knows about them. With the MCP server connected, the agent can answer questions that otherwise mean exporting three CSV files and matching emails by hand.
Cursor reads MCP servers from a JSON file, either per user or per project. Remote servers take a url and optional headers.
Setup
You need a secret key (nv_live_…) from Dashboard → API keys.
01Add the server to mcp.json
~/.cursor/mcp.json (or .cursor/mcp.json in a project)
{
"mcpServers": {
"nordva": {
"url": "https://mcp.nordva.dev",
"headers": {
"Authorization": "Bearer nv_live_..."
}
}
}
}Use the file in your home directory if the key should not live in the repository. A project-level .cursor/mcp.json containing a key belongs in .gitignore.
02Check the connection
Open Cursor Settings and find the MCP section. nordva should be listed with its tools and a green status. In Agent mode, ask it to run setup_check.
Then ask Cursor
- Which of my waitlist signups are paying customers now?
- What has [email protected] asked for, and is she paying?
- List paying customers who came from Product Hunt.
- Mark [email protected] as in talks and note that she wants SSO.
For “Which paying customers left feedback?” Cursor calls list_contacts with:
{
"paying": true,
"has_feedback": true,
"limit": 10
} Returned: Contacts with id, email, source, waitlist status, subscriber flag, feedback count, Stripe status, plan, MRR and stage, plus a cursor when there are more.
Contacts tools and arguments
list_contacts
Read-onlyList people across waitlist, feedback, subscribers and Stripe
| paying | optional | boolean | |
| has_feedback | optional | boolean | |
| status | optional | "waiting" | "invited" | "unsubscribed" | |
| source | optional | string, max 255 | |
| stage | optional | "potential" | "in_talks" | "customer" | "recurring" | "not_a_fit" | "inactive" | |
| q | optional | string, max 254 | Part of an email address. |
| limit | optional | number, 1–100 | |
| cursor | optional | string |
get_contact
Read-onlyGet one person across every primitive
| contact_id | required | string, max 64 | Contact id, e.g. "ct_...". |
update_contact
Set a contact's stage or note
| contact_id | required | string, max 64 | |
| stage | optional | "potential" | "in_talks" | "customer" | "recurring" | "not_a_fit" | "inactive" | |
| note | optional | string, max 5,000 |
Behaviour worth knowing
- People are matched on normalised email: lowercase, +alias removed, dots ignored on Gmail.
- update_contact changes only the stage and the note. It cannot change an email, a waitlist status or anything in Stripe, and a new note replaces the old one.
- It works without Stripe connected; every contact then has Stripe status none.
- On the Free plan only waitlist people are listed, in a single page.
- A contact is removed when the person no longer exists in any source, for example after their signup is deleted.
- Cursor's agent decides when to call a tool from the tool descriptions; naming the tool in the prompt ("use publish_changelog_entry") removes the guesswork.
- Tools that send email or remove content are flagged destructive by the server, and their descriptions tell the agent to confirm with you first.