mcp-server-product-studio
Officialmcp-server-product-studio
A full-feature MCP server for the Connext platform — a small product-development copilot. It shows how to combine, in one server:
🔐 Its own login — the server is its own OAuth 2.1 provider with a simple username/password page (same pattern as
mcp-server-example).🔎 A RAG tool — semantic-ish search over product documents (customer interviews, feature requests, PRDs, competitor notes) with a dependency-free BM25 retriever.
🗄️ A SQL tool — a read-only SQL query over a seeded product database (features / experiments / metrics).
📊 A chart MCP App — tools that render an SVG chart (in the chat), derived from the product data.
📝 A writable form MCP App — a feedback form the user fills in on the Connext side; on Save it calls a tool back over the app bridge and the server persists the note, then re-renders the saved list.
👤 Per-user identity — tools run as the signed-in user (e.g. "my features").
It's fully self-contained: a seeded in-memory SQLite database + an in-memory doc corpus, so it clones and runs with zero external services. Built on FastMCP.
What it demonstrates
Capability | Tool(s) | Backed by |
Retrieve unstructured context (the "why") |
|
|
Query structured records (the "what/when") |
|
|
Visualise the data |
|
|
Capture input that persists |
|
|
Know who's asking | all of them | own OAuth login ( |
The demo interactions that show them working together:
"What are customers saying about onboarding?" → RAG
"Show build-stage features ranked by RICE" → SQL → chart
"How is the Onboarding checklist doing?" → SQL + adoption chart
"Log some feedback about SSO" → form → the note is saved and shown
"Draft next sprint's priorities" → RAG (pain points) + SQL (backlog)
Quick start
Requires Python 3.11+.
# 1. install
python -m venv .venv && source .venv/bin/activate
pip install -e .
# 2. run
python server.py
# -> serving on http://localhost:8000 (MCP endpoint: http://localhost:8000/mcp/)
# 3. in another terminal, connect the way a client does (opens a browser login)
python examples/connect_with_client.py
# sign in as alice / password123 (or priya / hunter2)Demo users are product managers whose usernames own features in the data, so "my features" works:
username | password | owns |
|
| Onboarding, Billing, SSO, … |
|
| Growth, Activation, Dashboard, … |
The tools
search_product_docs(query, limit=3) — BM25 retrieval over the doc corpus in
rag.py (customer interviews, feature requests, support themes, competitor
notes, PRDs). Returns the most relevant passages with scores.
describe_data() → run_sql(sql) — the model calls describe_data for the
schema, then run_sql with a SELECT. The query runs against a SQLite file
opened read-only (mode=ro) and is additionally checked to be a single
read-only statement — a tool can never mutate the data. The signed-in username is
available for WHERE owner = '<username>'.
chart_roadmap(owner=None) and chart_feature_adoption(feature) — MCP
Apps. Each returns a text summary (what the model reads) and
structuredContent (the chart data). Connext reads the shared
ui://product-studio/chart template and its JavaScript draws the SVG from that
data over the app bridge (see MCP Apps).
feedback_form() → log_feedback(feature, sentiment, note) — a writable
MCP App. feedback_form opens the form with the feature list + recent notes; on
Save, the form's JS calls log_feedback back over the tools/call bridge, which
does a single parameterized INSERT (a read-write connection — run_sql stays
read-only) and returns the updated list. Mark log_feedback app-callable in
Connext so the form is allowed to call it.
The data (all seeded, self-contained)
db.py — three tables modelling a team building a SaaS product:
features(id, title, stage, priority, rice_score, effort_weeks, owner, target_release)
stage ∈ idea → discovery → design → build → beta → ga (NPD stage-gate)
experiments(id, feature_id, hypothesis, metric, control, variant, lift_pct, status)
metrics(feature_id, week, adoption, retention) (weekly time series)rag.py — ~10 short product documents (interviews, requests, PRDs, competitor
notes) that reference the same features, so RAG and SQL tell a joined story.
MCP Apps: charts (read) + a form (write)
An MCP App is a ui:// HTML resource the host renders in a sandboxed iframe.
Connext reads the resource (resources/read) and talks to it over the SEP-1865
JSON-RPC bridge (postMessage), so the drawing/logic lives in the template's
inline JS — no external scripts or assets (strict CSP) — and it reads the
host's var(--mcp-color-*) theme tokens. Each template also reports its height
via ui/notifications/size-changed so the host fits the iframe to the content.
Charts (read-only) — charts.py. The tool sends the chart data as
structuredContent; the host delivers it to the template
(ui/notifications/tool-result) and the JS renders the SVG. The two-series
colours are a validated categorical palette (blue/aqua, checked for
colour-blind separation and contrast).
Form (writable) — feedback.py. Same bridge, plus the write direction:
on Save the form calls a tool back with tools/call, which the host proxies to
the server (gated by the app-callable allowlist):
// inside the form template — call the server's write tool over the bridge
parent.postMessage({ jsonrpc: "2.0", id: 7, method: "tools/call",
params: { name: "log_feedback",
arguments: { feature, sentiment, note } } }, "*");
// host replies with the CallToolResult -> re-render the saved listThe server persists the note and returns the updated list, which the form shows — the full form → persist → read-back loop, driven from the Connext side.
Connecting it to Connext
Same as mcp-server-example — this server is its own OAuth provider, so Connext
drives standard OAuth 2.1 with dynamic client registration:
Expose the server on a public HTTPS URL and run it with
PUBLIC_URLset to that URL (every OAuth discovery endpoint is built from it).Register it in Connext (Admin → MCP Servers → Add): URL
https://<your-host>/mcp, Transport HTTP, Auth OAuth, client id/secret blank (dynamic registration handles it). Enable Allow UI so the apps render, and marklog_feedbackapp-callable so the feedback form is allowed to save.Connect as a user — click Connect, sign in on this server's login page, and the agent can call the tools as that user.
Taking it to production
This example keeps everything in memory / in a demo file so it's easy to read. For a real deployment:
Users: replace
DEMO_USERSinauth.pywith your real user store + hashed passwords (or delegate to SSO — see the siblingmcp-server-entra-example).SQL: point
db.pyat your real warehouse (Postgres/Snowflake/…) and keep the read-only + single-statement guardrails (add query timeouts + row limits).RAG: swap the in-memory BM25 for a real vector store + embeddings (pgvector, etc.) and chunk your documents.
Charts: the SVG builders scale fine; add a hover/tooltip layer for richer interactivity if your host allows scripts in the MCP App iframe.
Tokens/HTTPS: persist tokens (or issue signed JWTs) and terminate TLS in front; set
PUBLIC_URLto thehttps://URL.
The files
File | What it does |
| Entry point: seeds the DB, builds the FastMCP server with its own OAuth login, registers tools + |
| The OAuth provider + login page + demo users (from |
| Seeded SQLite: |
| The document corpus + a dependency-free BM25 retriever. |
| The seven tools: |
| The dynamic chart MCP App template (SVG drawn in-browser from the tool's data over the app bridge). |
| The writable feedback-form MCP App template (calls |
| A client that runs the same OAuth flow Connext does. |
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/connextai/mcp-server-product-studio'
If you have feedback or need assistance with the MCP directory API, please join our Discord server