Feedback MCP

Feedback MCP

Collect and analyze user feedback from any app via a single API endpoint, with MCP tools for listing, searching, and stats.

Category
访问服务器

README

<p align="center"> <img src="assets/exports/banner/banner.png" alt="Feedback MCP: collect user feedback from any app, analyze it with Claude" width="100%" /> </p>

<p align="center"> <a href="https://github.com/Parra-Inc/feedback-mcp/actions/workflows/ci.yml"><img src="https://github.com/Parra-Inc/feedback-mcp/actions/workflows/ci.yml/badge.svg" alt="CI" /></a> <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-34d399" alt="MIT License" /></a> <img src="https://img.shields.io/badge/PRs-welcome-34d399" alt="PRs welcome" /> <img src="https://img.shields.io/badge/Next.js-16-black" alt="Next.js 16" /> <img src="https://img.shields.io/badge/Prisma-7-2D3748" alt="Prisma 7" /> <img src="https://img.shields.io/badge/MCP-streamable_http-7dd3fc" alt="MCP" /> </p>

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/feedback from 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 dependencies

Prefer PostgreSQL?

docker compose -f docker-compose.postgres.yml up -d

The 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 Deploy to Render (uses render.yaml: web service + managed Postgres, auto-generated MCP_SECRET)
Vercel Deploy with Vercel (bring a Postgres URL, e.g. from Neon; SQLite does not persist on serverless)
Anywhere with Docker docker build -f apps/server/Dockerfile . and run with the env vars below. Fly.io, Railway, a VPS: anywhere a container and a volume (or a Postgres) live.

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
list_projects All configured projects with their platforms and forms
get_project One project by slug
list_forms / get_form Form definitions, including field schemas
list_feedback Feedback for a project, newest first, with form / platform / date filters and cursor pagination
get_feedback A single submission by id
search_feedback Full-text search across submission data and metadata
feedback_stats Counts grouped by platform, form, or day

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.json

project.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
slug yes Must match the directory name
name yes Display name
description no Shown in the read API and MCP
platforms no Allowed platform values for submissions. Omit to allow any.
ingestKeys yes Keys that authorize submissions. secretEnv names the env var holding the secret, so no secrets live in git.
auth.jwt no Verify end-user tokens on submission (see below)
slackWebhookEnv no Env var naming a per-project Slack webhook (overrides SLACK_WEBHOOK_URL)

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 min, max, pattern string with length / regex constraints
number min, max number in range
boolean boolean
enum values (required) one of the listed strings
email email address
url URL
date 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 X-Feedback-Key: <key> Submitting feedback to one project. Safe to embed in clients as a spam deterrent; treat it as public.
End-user JWT (optional) Authorization: Bearer <jwt> on submission Attaches a verified user identity (sub) to the feedback. You configure how your tokens are verified per project: jwksUrl, publicKeyEnv (PEM), or secretEnv (HMAC), plus optional issuer, audience, algorithms, and required.
MCP secret Authorization: Bearer <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
POST /api/v1/feedback Submit feedback: { project, form, platform?, data, metadata? }

Read and manage (requires Authorization: Bearer <MCP_SECRET>):

Method Path Description
GET /api/v1/projects List projects
GET /api/v1/projects/:slug One project
GET /api/v1/projects/:slug/forms Forms for a project
GET /api/v1/projects/:slug/feedback Feedback with form, platform, since, until, limit, cursor query params
DELETE /api/v1/projects/:slug/feedback Bulk delete with user, form, platform, before filters (or all=true). Covers GDPR erasure requests.
GET /api/v1/projects/:slug/export Stream every submission as NDJSON (backups, portability)
GET /api/v1/feedback/:id One submission
DELETE /api/v1/feedback/:id Delete one submission
GET /api/health 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 user handles GDPR/CCPA erasure requests.
  • Export: stream a project's entire history as NDJSON for backups or offline analysis.
  • Retention: set FEEDBACK_RETENTION_DAYS and feedback older than the window is deleted automatically (swept at most hourly, piggybacking on ingest traffic; no cron needed).

Environment variables

Variable Required Description
MCP_SECRET yes Bearer secret for the MCP server, admin API, and OAuth flow. openssl rand -hex 32
DATABASE_PROVIDER no postgresql (default) or sqlite
DATABASE_URL no Connection string. Defaults: local Postgres on :5457, or file:./data/feedback.db for SQLite
SLACK_WEBHOOK_URL no Slack incoming webhook; every submission is cross-posted after the database write
RATE_LIMIT_IP_PER_MINUTE no Ingest requests per minute per client IP (default 60, 0 disables)
RATE_LIMIT_PROJECT_PER_MINUTE no Ingest requests per minute per project (default 600, 0 disables)
FEEDBACK_RETENTION_DAYS no Auto-delete feedback older than this many days (unset keeps everything)
PUBLIC_URL no Public origin used in OAuth discovery metadata when behind a proxy, e.g. https://feedback.your-domain.com
CONFIG_DIR no Override the config directory (default config/ in the app root)
per-project vars Whatever your project.json files reference via secretEnv, slackWebhookEnv, publicKeyEnv

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-pg driver adapter, with real migration history (prisma migrate deploy runs 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 :3060
  • SQLite instead: DATABASE_PROVIDER=sqlite pnpm db:sync && DATABASE_PROVIDER=sqlite ... pnpm dev
  • Unit tests: pnpm --filter @feedback-mcp/server test
  • End-to-end smoke test: pnpm --filter @feedback-mcp/server smoke
  • Prisma Studio: pnpm --filter @feedback-mcp/server db:studio (:5560)
  • Marketing site: pnpm dev:site (:3061), deployed to GitHub Pages from apps/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_feedback MCP 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.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选