Developer reference
Mind the Product MCP
A read-only Model Context Protocol server over 15 years of product management writing, talks, events and jobs. Connect a client in one click on the setup page, or call it directly with the reference below.
Overview
- Endpoint
- https://www.mindtheproduct.com/api/mcp/
- Transport
- Streamable HTTP (JSON-RPC 2.0 over POST)
- Protocol version
- 2026-07-28
- Authentication
- OAuth 2.1 — authorization code + PKCE
- Scope
- mcp:read
- Capabilities
- tools, resources
- Server name
- mind-the-product
The trailing slash matters. This site runs with trailingSlash: true, so /api/mcp 308-redirects — and not every MCP client follows a 308 on POST. Configure the URL exactly as shown.
Quickstart
Every request is a JSON-RPC 2.0 POST. Start unauthenticated to see where to authorize — the 401 tells you.
curl -isL -X POST https://www.mindtheproduct.com/api/mcp/ \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# HTTP/2 401
# www-authenticate: Bearer realm="mtp-mcp",
# resource_metadata="https://www.mindtheproduct.com/.well-known/oauth-protected-resource"curl -s https://www.mindtheproduct.com/.well-known/oauth-protected-resource | jq
{
"resource": "https://www.mindtheproduct.com/api/mcp/",
"authorization_servers": ["https://www.mindtheproduct.com"],
"scopes_supported": ["mcp:read"],
"bearer_methods_supported": ["header"]
}curl -sL -X POST https://www.mindtheproduct.com/api/mcp/ \
-H "Authorization: Bearer $MTP_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_mtp_content",
"arguments": { "query": "product discovery", "limit": 3 }
}
}'Results come back as MCP content blocks. The text is markdown: a ranked source list with URLs, followed by the matching passages.
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "## Search Results for \"product discovery\"\n\n### Sources\n[1] ..."
}
]
}
}Authentication
OAuth 2.1 with PKCE, and no pre-registration: your client registers itself, the user approves once, and you get a token bound to them. There is no API key and no client-credentials grant — every token belongs to a person who clicked approve.
| Endpoint | Spec | Purpose |
|---|---|---|
| /.well-known/oauth-protected-resource | RFC 9728 | Which authorization server protects /api/mcp. |
| /.well-known/oauth-authorization-server | RFC 8414 | Authorization, token, registration and revocation endpoints. |
| /api/mcp/oauth/register | RFC 7591 | Dynamic client registration. No credentials needed. |
| /oauth/authorize | OAuth 2.1 | Consent screen. Open this in the user’s browser. |
| /api/mcp/oauth/token | OAuth 2.1 | Authorization code and refresh token grants. |
| /api/mcp/oauth/revoke | RFC 7009 | Revoke an access or refresh token. |
Register a client
curl -s -X POST https://www.mindtheproduct.com/api/mcp/oauth/register/ \
-H 'Content-Type: application/json' \
-d '{
"client_name": "My Product Research Bot",
"redirect_uris": ["http://localhost:8765/callback"]
}'
# { "client_id": "…", "token_endpoint_auth_method": "none", "scope": "mcp:read" }Public clients only. redirect_uris must be HTTPS, or http://localhost for local development.
Get a token
Send the user to the consent screen with an S256 PKCE challenge, then exchange the code they come back with.
https://www.mindtheproduct.com/oauth/authorize/
?client_id=YOUR_CLIENT_ID
&redirect_uri=http://localhost:8765/callback
&response_type=code
&scope=mcp:read
&state=RANDOM
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256curl -s -X POST https://www.mindtheproduct.com/api/mcp/oauth/token/ \
-d grant_type=authorization_code \
-d code=THE_CODE \
-d redirect_uri=http://localhost:8765/callback \
-d client_id=YOUR_CLIENT_ID \
-d code_verifier=YOUR_VERIFIER
# { "access_token": "mtp_at_…", "token_type": "Bearer",
# "expires_in": 3600, "refresh_token": "mtp_rt_…", "scope": "mcp:read" }Access tokens last an hour. Refresh tokens last 30 days and rotate on every use — keep the newest one, and expect a replayed refresh token to revoke the whole grant.
Tools
Six read-only tools. Call them with tools/call, or list them at runtime with tools/list. The four content tools also return structuredContent matching their outputSchema, so you can parse results instead of scraping the markdown.
search_mtp_content
Hybrid semantic + keyword search across 1,500+ articles, conference talks and guides. Returns ranked sources with URLs, plus the matching passages.
| query* | string | Natural language search query. |
| source_type | "article" | "video" | "podcast" | Filter by content type. Omit to search everything. |
| limit | number | Default 6, capped at 20. |
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_mtp_content",
"arguments": {
"query": "how to run a discovery sprint",
"source_type": "article",
"limit": 3
}
}
}get_article
Fetch the full text of one article, with author and date metadata.
| slug* | string | URL path, with or without the leading slash. |
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_article",
"arguments": {
"slug": "/what-is-product-management"
}
}
}list_recent
Recently published articles, newest first.
| limit | number | Default 10, capped at 20. |
| topic | string | Optional topic filter. |
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_recent",
"arguments": {
"topic": "AI",
"limit": 5
}
}
}search_events
MTP conferences, training workshops and ProductTank meetups worldwide.
| query | string | Omit to get upcoming events generally. |
| upcoming_only | boolean | Default true. Set false to include past events. |
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_events",
"arguments": {
"query": "ProductTank London"
}
}
}search_jobs
Live listings from the Mind the Product job board.
| query* | string | Role, skill or keyword. |
| location | string | City, country, or "remote". |
| limit | number | Default 10, capped at 20. |
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_jobs",
"arguments": {
"query": "senior product manager",
"location": "remote",
"limit": 5
}
}
}* required
Resources
Articles are exposed as resources, so getting full text does not need a second tool call or a guessed slug. Every search hit comes back with a resource_link alongside the prose — read that URI and you get the whole article.
{
"type": "resource_link",
"uri": "mtp://article/why-great-product-managers-think-like-therapists",
"name": "Why great product managers think like therapists",
"mimeType": "text/markdown"
}curl -sL -X POST https://www.mindtheproduct.com/api/mcp/ \
-H "Authorization: Bearer $MTP_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "resources/read",
"params": { "uri": "mtp://article/some-article-slug" }
}'The scheme is mtp:// rather than https:// deliberately: articles render through a CMS, so the markdown only exists once the server has extracted it — a client cannot fetch it unaided.resources/templates/list advertises mtp://article/{slug}, and resources/list pages through recent articles.
Errors
Transport problems come back as HTTP status codes. Problems inside a call come back as JSON-RPC errors, or as a content block with isError: true when a tool itself fails.
| 401 | HTTP | No or invalid credentials. Read WWW-Authenticate for where to authorize. |
| 429 | HTTP | Rate limited. Back off and retry. |
| -32600 | JSON-RPC | Malformed request — missing or wrong jsonrpc envelope. |
| -32601 | JSON-RPC | Unknown method. |
| -32602 | JSON-RPC | Invalid params, e.g. a tools/call with no tool name. |
Rate limits
Limits are per IP, per minute. tools/call is limited harder than the rest because each search runs an embedding and a vector query.
tools/call— 30 requests per minuteeverything else— 120 requests per minute
Exceeding a limit returns 429. Building something that needs more? Email support@mindtheproduct.com.