Feedback MCP
OfficialProvides optional cross-posting of every feedback submission to a configured Slack team channel via webhook.
Click on "Install 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 or SQLite, 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:3000. Check it:
curl http://localhost:3000/api/health
# {"status":"ok","database":"ok","config":"ok","projects":1}Submit your first feedback:
curl -X POST http://localhost:3000/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 |
|
Anywhere with Docker |
|
Related MCP server: Feedback Synthesis 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:
PostgreSQL (default): production-ready, uses the
@prisma/adapter-pgdriver adapter, with real migration history (prisma migrate deployruns on container start).SQLite: perfect for a single container with a volume. Zero external services. Schema is applied with
prisma db push, which refuses destructive changes.MongoDB: on the roadmap, currently blocked on a Prisma 7 driver adapter.
The provider is baked into the Prisma schema at generate time; pnpm db:sync (or the Docker entrypoint) rewrites it from DATABASE_PROVIDER automatically.
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 up # local Postgres on :5457 (or use sqlite below)
pnpm db:sync # generate client + push schema
MCP_SECRET=dev EXAMPLE_APP_INGEST_KEY=dev pnpm dev # server on :3060SQLite instead:
DATABASE_PROVIDER=sqlite pnpm db:sync && DATABASE_PROVIDER=sqlite ... pnpm devUnit 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(:5560)Marketing site:
pnpm dev:site(:3061), 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 installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- 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/Parra-Inc/feedback-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server