Product Manager MCP
Integrates with GitHub to read repository activity and release information, supporting weekly project updates and customer-facing release notes from merged work.
Integrates with Gmail to send interview scheduling confirmations and notify customers when requested features ship.
Provides Google OAuth-based connection for Google Workspace services, enabling workflows across Google Docs, Sheets, Forms, Calendar, and Gmail.
Integrates with Google Calendar to schedule retros, customer interviews, and launch-day events.
Integrates with Google Docs to draft PRDs from discovery calls and write cycle review documents.
Integrates with Google Forms to collect feedback and interview screener responses for triage, research, and scheduling workflows.
Integrates with Google Sheets to maintain roadmap spreadsheets, log triaged feedback, and rank feature requests by revenue.
Integrates with Linear to create and dedupe issues, manage projects and milestones, sync customers, and generate digests from calls, specs, feedback, and shipped work.
Integrates with Notion to turn specs into Linear issues, synthesize research into pages, create launch rooms, and draft release notes.
Integrates with Slack to post project updates, feedback triage confirmations, competitor alerts, launch channels, and Linear digests.
Integrates with Stripe to rank feature requests by revenue, sync Linear customer tiers from billing, and use MRR data for prioritization.
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., "@Product Manager MCP turn yesterday's call notes into deduped Linear issues and post them to #product"
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.
Product Manager Workflows
Calls, specs and feedback become deduped Linear issues, PRDs and weekly project updates.
An MCP server with 20 workflows across Granola, Linear, Slack, Google Docs, Notion, GitHub, Google Sheets, Google Forms, Firecrawl, Google Calendar, Gmail, Stripe and Tavily. Each workflow is a prompt your agent runs as a slash command, over the 49 tools it needs and no others.
uv tool install https://github.com/r28ai/product-manager-mcp/releases/download/v0.1.0/product_manager_mcp-0.1.0-py3-none-any.whl
claude mcp add pm -- product-manager-mcpIt installs with uv from this repository's release, with no git and nothing to build; nothing but Charter and the libraries it uses comes from PyPI. To update, run the install line from the latest release. If a desktop app cannot find product-manager-mcp, give it the full path from which product-manager-mcp (where product-manager-mcp on Windows).
Then ask your agent to connect your apps, or run /mcp__pm__setup.
Connect your apps
Ask the agent to connect one ("connect Linear"). It tells you where to get that app's key and the command that stores it, and the next call works, with no restart. The agent never asks for a key in the chat.
Or connect everything this server uses from a terminal:
product-manager-mcp login # each app in turn
product-manager-mcp login granola # just one
product-manager-mcp status # what is connectedTokens and keys go to your operating system's keychain (macOS Keychain, Windows Credential Manager, the Secret Service on Linux), and are checked with one read-only call to the app's own API before they are kept. Every key, token and OAuth client is yours: we register no app with any of these services, and nothing passes through a server of ours, because there isn't one.
App | How it connects | Or set |
Granola | Your own key (get one), entered once. In the Granola app: Settings → Connectors → API keys. Business plan or above. |
|
Linear | Your own key (get one), entered once. |
|
Slack | Your own key (get one), entered once. A bot token from your own Slack app, which the guide sets up in about three minutes. |
|
Browser sign-in, over your own OAuth client (make one). |
| |
Notion | Your own key (get one), entered once. Then share the pages it should see with the integration. |
|
GitHub | Your own key (get one), entered once. |
|
Firecrawl | Your own key (get one), entered once. |
|
Stripe | Your own key (get one), entered once. |
|
Tavily | Your own key (get one), entered once. |
|
A variable set in your client's config always wins over the keychain.
Related MCP server: Chief of Staff
Workflows
Workflow | What you get | Apps |
Call notes → deduped Linear issues | Feature requests from yesterday's calls land as issues, matched against what already exists, and #product sees the list. | Granola, Linear, Slack |
Customer requests attached to the issue they ask for | The roadmap gets ranked by who asked, not by who spoke loudest in planning. | Granola, Linear |
Discovery calls → PRD draft | A first PRD drafted from five interviews, with quotes, before the PM opens a blank doc. | Granola, Google Docs |
PRD doc → Linear project, milestones and issues | The spec turns into a plan in one pass instead of an afternoon of copy-paste. | Google Docs, Linear |
Notion spec → Linear issues, linked back | Teams that write in Notion and ship in Linear stop keeping two lists. | Notion, Linear |
Weekly project update, written from the work | The update reports what merged and what slipped, with health set from evidence rather than optimism. | Linear, GitHub, Slack |
Roadmap sheet that keeps itself true | Leadership keeps its spreadsheet and the spreadsheet stops lying. | Linear, Google Sheets |
Feedback form → triaged customer needs | Every response is either attached to an issue or logged as new, never left in a tab. | Google Forms, Linear, Google Sheets |
#feedback channel → Linear | Feedback posted in Slack gets an issue and a checkmark, so nobody wonders if it was seen. | Slack, Linear |
Competitor changelog watch | A competitor ships something and a scoped issue exists before the sales team asks about it. | Firecrawl, Linear, Slack |
Cycle review doc and retro booking | Planned vs. done vs. carried over, written up and on the calendar before the retro. | Linear, Google Docs, Google Calendar |
Customer interview program | Screener, scheduling and confirmations for ten interviews without a scheduling tool. | Google Forms, Google Calendar, Gmail |
Research synthesis from a folder of calls | Twenty calls become one synthesis page with every claim traceable to a note. | Granola, Notion, Linear |
Requests ranked by revenue | Each feature request carries the MRR of the customers behind it. | Linear, Stripe, Google Sheets |
Linear customers synced from Stripe | Linear's customer tiers and revenue reflect billing, so prioritisation uses real numbers. | Stripe, Linear |
Shipped → tell everyone who asked | The loop gets closed with every requester, which is the cheapest retention there is. | Linear, Gmail |
Launch room in one go | Checklist page, launch-day event and channel with the right people, from the project. | Linear, Notion, Google Calendar, Slack |
Problem-space research brief | A cited brief lands in the project's docs before the kickoff meeting. | Tavily, Linear, Slack |
Release notes for customers | Engineering's release notes rewritten for users, from the issues that actually closed. | Linear, GitHub, Notion, Slack |
Linear digest to Slack | What moved, what's stuck, who owns it, with people actually @-mentioned. | Linear, Slack |
Every prompt takes one optional argument, details: the repo, team, channel, customer or date range you mean, so the agent does not have to ask. In Claude Code, put it in quotes, or only its first word arrives:
/mcp__pm__call_notes_to_deduped_linear_issues "this week's customer calls, Linear team PROD"Reads run without asking. Before anything that creates, sends, changes or deletes, the prompt tells the agent to show you the call and wait.
8 of the 20 workflows need no Google or Granola credential.
Other clients
Claude Desktop: install uv if you have not, since Claude Desktop starts the server with it, then open the .mcpb from the latest release. Claude asks for any keys in its own settings and keeps them in your keychain. The first start takes a few seconds longer, while uv installs it.
VS Code (.vscode/mcp.json): VS Code asks for each key the first time the server starts and stores it securely. Leave out any you stored with login.
{
"inputs": [
{
"type": "promptString",
"id": "granola-api-key",
"description": "Granola: API key",
"password": true
},
{
"type": "promptString",
"id": "linear-api-key",
"description": "Linear: Personal API key",
"password": true
},
{
"type": "promptString",
"id": "slack-bot-token",
"description": "Slack: Bot token (xoxb-\u2026)",
"password": true
},
{
"type": "promptString",
"id": "google-client-secret",
"description": "Google: OAuth client secret",
"password": true
},
{
"type": "promptString",
"id": "notion-api-key",
"description": "Notion: Integration secret (ntn_\u2026)",
"password": true
},
{
"type": "promptString",
"id": "github-token",
"description": "GitHub: Personal access token",
"password": true
},
{
"type": "promptString",
"id": "firecrawl-api-key",
"description": "Firecrawl: API key",
"password": true
},
{
"type": "promptString",
"id": "stripe-api-key",
"description": "Stripe: Secret or restricted key",
"password": true
},
{
"type": "promptString",
"id": "tavily-api-key",
"description": "Tavily: API key",
"password": true
}
],
"servers": {
"pm": {
"type": "stdio",
"command": "product-manager-mcp",
"env": {
"GRANOLA_API_KEY": "${input:granola-api-key}",
"LINEAR_API_KEY": "${input:linear-api-key}",
"SLACK_BOT_TOKEN": "${input:slack-bot-token}",
"GOOGLE_CLIENT_SECRET": "${input:google-client-secret}",
"NOTION_API_KEY": "${input:notion-api-key}",
"GITHUB_TOKEN": "${input:github-token}",
"FIRECRAWL_API_KEY": "${input:firecrawl-api-key}",
"STRIPE_API_KEY": "${input:stripe-api-key}",
"TAVILY_API_KEY": "${input:tavily-api-key}",
"GOOGLE_CLIENT_ID": ""
}
}
}
}Cursor (.cursor/mcp.json) starts it the same way:
{
"mcpServers": {
"pm": {
"command": "product-manager-mcp"
}
}
}Codex (~/.codex/config.toml) starts a turn without waiting for a server unless it is required, and then the agent has none of its tools. required = true makes the session wait for it, and startup_readiness = "catalog" waits for its tool list rather than just its connection:
[mcp_servers.pm]
command = "product-manager-mcp"
required = true
startup_readiness = "catalog"
startup_timeout_sec = 30Name the server pm. A host builds each tool's name from that key, and a longer one can push a tool past the 64 characters a function name allows.
Built with Charter
Every tool here is a Charter declaration: a Pydantic schema saying where each field goes on the wire. Charter's runtime builds the request, attaches and refreshes the credential, and trims the response before the model reads it. It runs in your process, with no proxy and no telemetry.
The 49 tool schemas come to 74,058 tokens.
The same tools work in your own agent, without MCP:
from charter.adapters.openai import to_openai_tools
from charter_packs_mcp import FAMILIES
tools = FAMILIES["product"].tools()
definitions = to_openai_tools(tools) # or charter.adapters.langchainNeed an API that isn't here? Write a pack: your coding agent writes the declarations, and Charter's conformance suite checks them.
Granola:
granola_notes_list,granola_notes_get,granola_notes_transcript_get,granola_folders_listLinear:
linear_search_issues,linear_issue_create,linear_customers_list,linear_customer_need_create,linear_project_create,linear_project_milestone_create,linear_project_get,linear_issues_list,linear_project_update_create,linear_projects_list,linear_project_milestones_list,linear_cycle_get,linear_customer_needs_list,linear_customer_create,linear_customer_update,linear_customer_get,linear_document_create,linear_users_listSlack:
slack_chat_post_message,slack_conversations_history,slack_reactions_add,slack_conversations_create,slack_conversations_invite,slack_users_listGoogle Docs:
gdocs_documents_create,gdocs_documents_batch_update,gdocs_documents_getNotion:
notion_pages_retrieve_markdown,notion_comments_create,notion_pages_createGitHub:
github_pulls_list,github_releases_generate_notesGoogle Sheets:
gsheets_spreadsheets_values_update,gsheets_spreadsheets_values_appendGoogle Forms:
gforms_forms_responses_list,gforms_forms_createFirecrawl:
firecrawl_monitor_create,firecrawl_monitor_checks_listGoogle Calendar:
gcalendar_events_insertGmail:
gmail_messages_send,gmail_drafts_createStripe:
stripe_customers_list,stripe_subscriptions_listTavily:
tavily_research_create,tavily_research_get
License
Apache 2.0.
Available Tools
51 toolsconnectA
Connect one app this server uses. For an app that issues keys, says where to get one and the terminal command that stores it. For Google, once the user's own OAuth client is set, starts the browser sign-in and returns at once: the user approves in the browser and the next call works. To see which apps are connected, call connection_status. Never ask the user for a key in the chat.
Inputs: app (required).
connection_statusARead-only
See which apps this server is connected to, and how to connect each one that is not. Changes nothing.
Inputs: no named parameters.
firecrawl_monitor_checks_listCRead-onlyIdempotent
List checks for a monitor.
Inputs: monitorId (required), limit, offset, status.
firecrawl_monitor_createB
Create a scheduled monitor for scrape, crawl, or search targets.
Inputs: name (required), targets (required), schedule (required), goal, +4 more.
gcalendar_events_insertC
Create a calendar event; returns details of the event.
Inputs: event (required), calendarId (required), sendUpdates, maxAttendees, +3 more.
gdocs_documents_batch_updateA
Apply a list of edits to a document. Each request sets exactly one kind of edit — insert_text, replace_all_text, update_text_style, insert_table and so on. Edits apply in order against a document that shifts as they do: when inserting at several indices, order the requests back-to-front so earlier insertions do not move the positions later ones refer to.
Inputs: body (required), documentId (required).
gdocs_documents_createA
Create a blank document with a title. Only the title is honoured — the document is created empty. To add content, call this and then documents_batch_update with the returned documentId.
Inputs: body.
gdocs_documents_getARead-onlyIdempotent
Read a document's full structural content. The document ID is the long string in its URL, between '/d/' and '/edit'. Pass include_tabs_content=true for a document with several tabs, since the default response covers only the first.
Inputs: documentId (required), includeTabsContent, suggestionsViewMode.
gforms_forms_createA
Create a new form from a title. Only the title and the document title are honoured: the form is created with no description, no items and default settings. To add questions, call this and then forms_batch_update with the returned formId. Pass unpublished=true to create a form that does not yet accept responses.
Inputs: body (required), unpublished.
gforms_forms_responses_listARead-onlyIdempotent
List a form's submitted responses, newest page first, up to 5000 per page. The only supported filter is on submission time: pass filter='timestamp >= 2026-01-01T00:00:00Z' to read what has arrived since a point in time. Answers come back keyed by questionId.
Inputs: formId (required), filter, pageSize, pageToken.
github_pulls_listARead-onlyIdempotent
List pull requests. Filter by state, by base branch, or by head in the form 'user:branch'.
Inputs: repo (required), owner (required), base, head, +5 more.
github_releases_generate_notesA
Generate release-note text from the pull requests merged since a previous tag. This creates nothing — GitHub returns a name and a markdown body and saves neither — so it is safe to call for a draft changelog. Pass the result to releases_create to publish it.
Inputs: repo (required), owner (required), tagName (required), previousTagName, +2 more.
gmail_drafts_createC
Save an email draft to Gmail.
Inputs: body (required), userId.
gmail_messages_sendC
Send an email via the Gmail API.
Inputs: body (required), userId.
granola_folders_listARead-onlyIdempotent
List the folders this key can reach, sorted alphabetically. The listing is flat and each folder names its parent in parent_folder_id, so a hierarchy is reassembled from a full walk. Use it to find the folder_id that narrows a note listing or a webhook endpoint's deliveries.
Inputs: cursor, pageSize.
granola_notes_getARead-onlyIdempotent
Read one meeting note: its AI summary, the people who attended, the calendar event it was taken against, and the folders it belongs to. Pass include='transcript' to get the transcript inline as well. A transcript too large to inline answers 413 TRANSCRIPT_TOO_LARGE. Read it with notes_transcript_get instead.
Inputs: noteId (required), include.
granola_notes_listARead-onlyIdempotent
List meeting notes, filtered by when they were created or last updated and optionally narrowed to one folder and its subfolders. Returns each note's id, title, owner and timestamps, not its content. Fetch that with notes_get. Only notes that already have a generated AI summary appear here.
Inputs: cursor, folderId, pageSize, createdAfter, +2 more.
granola_notes_transcript_getARead-onlyIdempotent
Read a meeting transcript one page at a time. Use this for any long transcript, and whenever notes_get answers 413 TRANSCRIPT_TOO_LARGE. Each item is one line of speech with who said it and when.
Inputs: noteId (required), cursor, pageSize.
gsheets_spreadsheets_values_appendC
Appends values to a spreadsheet.
Inputs: range (required), valueRange (required), spreadsheetId (required), valueInputOption (required), +4 more.
gsheets_spreadsheets_values_updateC
Sets values in a range of a spreadsheet.
Inputs: range (required), valueRange (required), spreadsheetId (required), valueInputOption (required), +3 more.
linear_customer_createB
Create a customer. Only name is required.
Inputs: variables (required).
linear_customer_getCRead-onlyIdempotent
Get one customer by its UUID.
Inputs: variables (required).
linear_customer_need_createA
Record a customer request, optionally attached to an issue or project. This is the one Linear mutation whose reply carries no object — it answers only with whether it worked.
Inputs: variables (required).
linear_customer_needs_listARead-onlyIdempotent
List customer requests. Filter by issue to see what a piece of work is wanted for, or by customer to see everything one company has asked for.
Inputs: variables.
linear_customers_listCRead-onlyIdempotent
List customers — the companies whose requests Linear tracks against issues.
Inputs: variables.
linear_customer_updateC
Update a customer's name, owner, tier, revenue or size.
Inputs: variables (required).
linear_cycle_getBRead-onlyIdempotent
Get one cycle with the issues in it.
Inputs: variables (required).
linear_document_createA
Create a document. Only title is required — a document can hang off a project, initiative, issue, cycle or team, or off nothing at all.
Inputs: variables (required).
linear_issue_createA
Create an issue. team_id and title are required; everything else is optional. The UUIDs for team, assignee, state and labels come from teams_list, users_list and workflow_states_list — Linear does not accept names here. Set parent_id to create a sub-issue.
Inputs: variables (required).
linear_issues_listARead-onlyIdempotent
List issues, optionally filtered. Conditions on one filter object combine with AND. To find a team's open work, filter on team.key and state.type.
Inputs: variables.
linear_project_createA
Create a project. name and at least one team UUID are required; team UUIDs come from teams_list.
Inputs: variables (required).
linear_project_getBRead-onlyIdempotent
Get one project with its content, milestones and members. Accepts the project's UUID or the slug from its URL.
Inputs: variables (required).
linear_project_milestone_createC
Create a milestone inside a project.
Inputs: variables (required).
linear_project_milestones_listARead-onlyIdempotent
List project milestones. Filter by project to get one project's.
Inputs: variables.
linear_projects_listBRead-onlyIdempotent
List projects in the workspace, with their status and progress.
Inputs: variables.
linear_project_update_createA
Post a status update on a project, optionally reporting health as 'onTrack', 'atRisk' or 'offTrack'. This writes a note; to change the project itself, use project_update.
Inputs: variables (required).
linear_search_issuesARead-onlyIdempotent
Search issues by text, across titles and descriptions. Set include_comments to search inside comments too. This is full-text search; to filter on fields such as state or assignee, use issues_list.
Inputs: variables (required).
linear_users_listARead-onlyIdempotent
List workspace members. Use this to resolve a person's name or email to the UUID that assignment expects.
Inputs: variables.
notion_comments_createA
Comment on a page or block, or reply to an existing discussion with its discussion_id. One or the other, never both.
Inputs: body (required).
notion_pages_createA
Create a page — as a subpage of another page, or as a row of a database by giving its data_source_id as the parent. Content comes as a markdown string Notion parses into blocks, or from a template: one or the other, never both.
Inputs: body (required), filterProperties.
notion_pages_retrieve_markdownARead-onlyIdempotent
Get a page's whole content as Markdown, rendered by Notion. One call instead of recursing through blocks_children_list, and the cheapest way to read a page you only need to understand rather than edit.
Inputs: pageId (required), includeTranscript.
slack_chat_post_messageA
Send a message to a Slack channel, private group, or DM. Provide text for a plain message; set thread_ts to reply inside an existing thread.
Inputs: channel (required), text, parse, blocks, +13 more.
slack_conversations_createB
Create a channel. Slack refuses a name that is already taken.
Inputs: name (required), isPrivate.
slack_conversations_historyBRead-onlyIdempotent
Fetch recent messages from a Slack channel.
Inputs: channel (required), limit, cursor, latest, +3 more.
slack_conversations_inviteA
Add people to a channel. With force set, Slack adds everyone it can and reports the rest alongside a success, so read the result.
Inputs: users (required), channel (required), force.
slack_reactions_addC
Add an emoji reaction to a Slack message.
Inputs: name (required), channel (required), timestamp (required).
slack_users_listARead-onlyIdempotent
List members of the workspace. Use this to resolve a person's name to the user ID that Slack mentions and filters expect.
Inputs: limit, cursor, teamId, includeLocale.
stripe_customers_listARead-onlyIdempotent
List customers, most recently created first. Filter by email to find one.
Inputs: email, limit, endingBefore, startingAfter.
stripe_subscriptions_listBRead-onlyIdempotent
List subscriptions. Filter by customer or status.
Inputs: limit, price, status, customer, +2 more.
tavily_research_createA
Create an async research task that searches, analyzes sources, and generates a cited report. Poll results with research_get.
Inputs: input (required), files, model, outputLength, +4 more.
tavily_research_getARead-onlyIdempotent
Retrieve the status and results of a research task by request_id. HTTP 202 means still running; poll until HTTP 200.
Inputs: requestId (required), includeUsage.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 51 tools
Most tools are clearly distinguished by service prefix and resource/action naming (e.g., linear_issues_list vs linear_search_issues, slack_chat_post_message vs slack_conversations_history). However, linear_project_update_create is misleading: it posts a status update on a project, not updates the project itself, and its description references a non-existent `project_update` tool, causing confusion.
Tool names consistently use snake_case with a service prefix and action verb at the end (e.g., linear_issue_create, slack_conversations_invite, gdocs_documents_get). Minor deviations exist: connect and connection_status lack a service prefix, and notion_pages_retrieve_markdown uses 'retrieve_markdown' while other read tools use 'get'.
With 51 tools spanning 14 different services, the set is far too large for a focused product manager server. While the breadth of integrations explains some volume, many tools are single-operation (e.g., only list or create) and could be consolidated, making the surface feel bloated and unwieldy.
Core CRUD operations are missing across nearly all resources: no update or delete for issues, projects, documents, Slack messages, or Gmail drafts. Several tools referenced in descriptions (linear_teams_list, linear_workflow_states_list, linear_project_update, gforms_forms_batch_update, github_releases_create) are absent, which will cause agent failures when following documented workflows.
Maintenance
Related MCP Connectors
Agent-complete, permission-scoped product operations for Priorify workspaces.
Competitive intelligence - track what competitors ship each week. 8 tools, 3 prompt workflows.
AI-native project management for tasks, docs, collaboration, and agents.
Agent personas for Claude. 16 tools, 13 personas, 3 workflows. Zero extra API cost. Free.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA multi-agent product-management copilot that automates morning briefings, metrics answers, knowledge lookups over an Obsidian vault, and PRD/ticket drafting.-
- AlicenseBqualityBmaintenanceRuns 20 slash-command workflows across Google Calendar, Gmail, Linear, Slack, Granola, Google Docs, GitHub, Stripe, Notion, Google Forms, Sheets and Drive to produce morning briefs, meeting prep, action items assigned to owners and weekly updates. Reads proceed without asking, while anything that creates, sends, changes or deletes is shown for approval first.471Apache 2.0
- AlicenseBqualityBmaintenanceEnables freelancers and agencies to run eight back-office workflows across Stripe, Google Drive, Linear, Google Calendar, Gmail, GitHub, Google Docs, Granola, Google Sheets and Firecrawl, covering client onboarding, invoices from calendar and commits, status reports, scope-creep detection, site audits and overdue invoice chasers. Reads run freely, while anything that creates, sends, changes or deletes is shown for approval first, with credentials kept in your own OS keychain and no proxying through any third-party server.28Apache 2.0
- AlicenseBqualityBmaintenanceRuns 13 agent slash-command workflows across Linear, GitHub, Google Docs, Slack, Firecrawl, Sheets, Tavily, Forms, Calendar, Gmail, Stripe, Granola and Notion — turning shipped features into blog and social drafts and handling competitor pricing, mention monitoring, SEO gaps, webinars, newsletters and launch-day tracking. Every credential is your own, kept in the OS keychain or client config, and anything that writes, sends or deletes is shown for approval first.29Apache 2.0