Feedback MCP
OfficialProvides optional cross-posting of every feedback submission to a configured Slack team channel via webhook.
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., "@Feedback MCPsummarize this week's bug reports"
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.
Feedback MCP is a free, open-source, self-hosted service for collecting and analyzing user feedback. Your apps submit feedback to one API endpoint. You (and your AI assistant) read it back through a built-in MCP server.
There is intentionally no dashboard. Projects and forms are declarative JSON config, feedback lives in your own database, and analysis happens in Claude (or any MCP client): "summarize this week's bug reports", "what are users asking for most on iOS?".
One endpoint in.
POST /api/v1/feedbackfrom iOS, Android, web, or any backend.MCP out.
list_feedback,search_feedback,feedback_stats, and more at/api/mcp.Forms as config. Each form declares a field schema; submissions are validated with Zod.
Your database. PostgreSQL, SQLite, or Cloudflare D1, chosen with one env var.
Slack cross-posting. Optional webhook posts every submission to your team channel.
Quickstart (Docker)
git clone https://github.com/Parra-Inc/feedback-mcp.git
cd feedback-mcp
cp apps/server/.env.example .env
# edit .env: set MCP_SECRET and EXAMPLE_APP_INGEST_KEY (openssl rand -hex 32)
docker compose up -d # SQLite, zero external dependenciesPrefer PostgreSQL?
docker compose -f docker-compose.postgres.yml up -dThe server listens on http://localhost:3065. Check it:
curl http://localhost:3065/api/health
# {"status":"ok","database":"ok","config":"ok","projects":1}Submit your first feedback:
curl -X POST http://localhost:3065/api/v1/feedback \
-H "Content-Type: application/json" \
-H "X-Feedback-Key: $EXAMPLE_APP_INGEST_KEY" \
-d '{
"project": "example-app",
"form": "bug-report",
"platform": "ios",
"data": {
"title": "Crash on launch",
"description": "The app closes immediately after opening.",
"severity": "high"
},
"metadata": { "appVersion": "1.2.0" }
}'
# {"feedback":{"id":"fb_...","createdAt":"..."}}One-click deploys
Platform | |
Render |
|
Vercel |
|
Cloudflare Workers | Serverless, no per-seat pricing, with a D1 (SQLite) database. |
Anywhere with Docker |
|
Related MCP server: Feedback Analyzer AI MCP
Connect Claude
The MCP server is served over streamable HTTP at /api/mcp. Two ways to authenticate:
claude.ai and Claude Desktop (OAuth)
Add a custom connector with the URL https://feedback.your-domain.com/api/mcp. The server implements the MCP OAuth flow (discovery, dynamic client registration, PKCE): claude.ai opens an approval page where you enter your MCP_SECRET once, and tokens are issued from there. Rotating MCP_SECRET revokes every issued token.
Claude Code
claude mcp add --transport http feedback https://feedback.your-domain.com/api/mcp \
--header "Authorization: Bearer <MCP_SECRET>"Any MCP client (.mcp.json)
{
"mcpServers": {
"feedback": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://feedback.your-domain.com/api/mcp",
"--header", "Authorization: Bearer ${MCP_SECRET}"
]
}
}
}MCP tools
Tool | What it does |
| All configured projects with their platforms and forms |
| One project by slug |
| Form definitions, including field schemas |
| Feedback for a project, newest first, with form / platform / date filters and cursor pagination |
| A single submission by id |
| Full-text search across submission data and metadata |
| Counts grouped by |
Configuration
Projects and forms are files, not database rows. The server loads and validates everything under config/ at boot (and on every request in dev), so adding a project is a pull request, and LLMs can edit your forms as easily as you can.
apps/server/config/
projects/
example-app/
project.json
forms/
bug-report.json
feature-request.jsonproject.json
{
"slug": "example-app",
"name": "Example App",
"platforms": ["ios", "android", "web"],
"ingestKeys": [{ "id": "default", "secretEnv": "EXAMPLE_APP_INGEST_KEY" }],
"auth": {
"jwt": {
"issuer": "https://auth.your-domain.com",
"audience": "example-app",
"algorithms": ["RS256"],
"jwksUrl": "https://auth.your-domain.com/.well-known/jwks.json",
"required": false
}
},
"slackWebhookEnv": "EXAMPLE_APP_SLACK_WEBHOOK"
}Field | Required | Description |
| yes | Must match the directory name |
| yes | Display name |
| no | Shown in the read API and MCP |
| no | Allowed |
| yes | Keys that authorize submissions. |
| no | Verify end-user tokens on submission (see below) |
| no | Env var naming a per-project Slack webhook (overrides |
Form files (forms/<slug>.json)
{
"slug": "bug-report",
"name": "Bug Report",
"fields": [
{ "name": "title", "type": "string", "required": true, "max": 120 },
{ "name": "description", "type": "string", "required": true },
{ "name": "severity", "type": "enum", "values": ["low", "medium", "high", "critical"] },
{ "name": "email", "type": "email" }
]
}Submissions are validated against the form's fields with Zod. Unknown keys are rejected.
Field type | Options | Validates as |
|
| string with length / regex constraints |
|
| number in range |
| boolean | |
|
| one of the listed strings |
| email address | |
| URL | |
| ISO 8601 date or datetime |
Every field also accepts label, description, and required (default false).
Authentication
Three separate credentials, three separate jobs:
Credential | Sent as | Grants |
Ingest key |
| Submitting feedback to one project. Safe to embed in clients as a spam deterrent; treat it as public. |
End-user JWT (optional) |
| Attaches a verified user identity ( |
MCP secret |
| Reading everything: the admin REST API and the MCP server. Keep it secret. |
REST API
Ingest (CORS-open, ingest key):
Method | Path | Description |
|
| Submit feedback: |
Read and manage (requires Authorization: Bearer <MCP_SECRET>):
Method | Path | Description |
|
| List projects |
|
| One project |
|
| Forms for a project |
|
| Feedback with |
|
| Bulk delete with |
|
| Stream every submission as NDJSON (backups, portability) |
|
| One submission |
|
| Delete one submission |
|
| Health check (no auth) |
Rate limiting
The ingest endpoint is rate limited out of the box: per client IP (default 60/min) and per project (default 600/min), returning 429 with a Retry-After header. Tune or disable with RATE_LIMIT_IP_PER_MINUTE and RATE_LIMIT_PROJECT_PER_MINUTE (0 disables). The limiter is in-process; if you run multiple replicas or serverless, add a shared limit at your reverse proxy.
Data lifecycle
Delete: remove a single submission by id, or bulk delete by user, form, platform, or age (see the table above). Deleting by
userhandles GDPR/CCPA erasure requests.Export: stream a project's entire history as NDJSON for backups or offline analysis.
Retention: set
FEEDBACK_RETENTION_DAYSand feedback older than the window is deleted automatically (swept at most hourly, piggybacking on ingest traffic; no cron needed).
Environment variables
Variable | Required | Description |
| yes | Bearer secret for the MCP server, admin API, and OAuth flow. |
| no |
|
| no | Connection string. Defaults: local Postgres on |
| no | Slack incoming webhook; every submission is cross-posted after the database write |
| no | Ingest requests per minute per client IP (default |
| no | Ingest requests per minute per project (default |
| no | Auto-delete feedback older than this many days (unset keeps everything) |
| no | Public origin used in OAuth discovery metadata when behind a proxy, e.g. |
| no | Override the config directory (default |
per-project vars | Whatever your |
See apps/server/.env.example for a documented template.
Databases
The Prisma schema is a single portable Feedback table, so switching providers is one env var (DATABASE_PROVIDER):
PostgreSQL (
postgresql, default): production-ready, uses the@prisma/adapter-pgdriver adapter, with real migration history (prisma migrate deployruns on container start).SQLite (
sqlite): perfect for a single container with a volume. Zero external services. Schema is applied withprisma db push, which refuses destructive changes.Cloudflare D1 (
d1): SQLite at the edge, via@prisma/adapter-d1, for the Cloudflare Workers deployment. See docs/DEPLOY-CLOUDFLARE.md.MongoDB: on the roadmap, currently blocked on a Prisma 7 driver adapter.
The SQL dialect is baked into the Prisma schema at generate time; pnpm db:sync (or the Docker entrypoint / build:cf) rewrites it from DATABASE_PROVIDER automatically (d1 uses the SQLite dialect).
Slack cross-posting
Set SLACK_WEBHOOK_URL (or a per-project slackWebhookEnv) and every accepted submission is posted to Slack as a Block Kit message with the project, form, platform, user, and submitted fields. Posting happens after the database write and never fails a request: if Slack is down, you just miss the ping, not the feedback.
Development
pnpm install
pnpm dev # frees ports, starts Postgres (:5452), syncs, runs server + siteOr run the pieces yourself:
pnpm up # local Postgres on :5452 (or use sqlite below)
pnpm db:sync # generate client + push schema
MCP_SECRET=dev EXAMPLE_APP_INGEST_KEY=dev pnpm dev:server # server on :3065SQLite instead:
DATABASE_PROVIDER=sqlite pnpm db:sync && DATABASE_PROVIDER=sqlite ... pnpm dev:serverUnit tests:
pnpm --filter @feedback-mcp/server testEnd-to-end smoke test:
pnpm --filter @feedback-mcp/server smokePrisma Studio:
pnpm --filter @feedback-mcp/server db:studio(:5572), orpnpm dev --studioMarketing site:
pnpm dev:site(:3066), deployed to GitHub Pages fromapps/site
Repo layout:
apps/server the self-hosted app: ingest API + read API + MCP server + OAuth
apps/site the marketing one-pager (static export, GitHub Pages)
assets open-assets project for the banner and social images
examples copy-paste client snippets (Swift, TypeScript)See CONTRIBUTING.md for the full guide and SECURITY.md for reporting vulnerabilities. Client integration snippets live in examples/.
Roadmap and non-goals
Planned:
MongoDB support (blocked on a Prisma 7 driver adapter)
A
delete_feedbackMCP tool (destructive operations are REST-only for now)Drop-in feedback form widgets
Non-goals, by design:
A web dashboard. The read API, MCP tools, and Slack are the interface. Your AI assistant is the dashboard.
A hosted SaaS. Feedback MCP is self-hosted; your feedback lives in your database.
License
MIT © Parra, Inc.
This server cannot be deployed
Maintenance
Related MCP Connectors
Analyze customer feedback at scale — reviews, surveys, calls. AI-powered themes and sentiment.
App intelligence across Google Play and the App Store: details, reviews, and search in one API.
Anonymous feedback from real people by voice or text, with an AI report. Flat price per campaign.
- AgentCatOAuthcom.agentcat
Analytics and debugging for your MCP server — explore usage and sessions, then root-cause errors.
Related MCP Servers
- AlicenseAqualityDmaintenanceMCP server that synthesizes user feedback from multiple sources into actionable insights. Aggregates reviews, support tickets, and survey responses to identify patterns and priorities.421 PyPIMIT
- AlicenseNot gradedqualityBmaintenanceFeedback Analyzer AI - MCP server providing AI-powered tools and automation by MEOK AI Labs11 npm60 PyPIMIT
- AlicenseBqualityBmaintenanceHosted MCP server for YaniFend, a WordPress feedback widget. Manage your questionary (list/create/update/delete questions and options) and read collected answers via natural language.102MIT
- AlicenseNot gradedqualityDmaintenanceWeb-based feedback collection tool for MCP environments with Markdown, Mermaid charts, and image upload support.2MIT