KI-Assistenten

Connect an AI assistant (MCP)

Point Claude, ChatGPT or your own agent at the Simple Feedback MCP server to build surveys and read results, signing in from the assistant or with an API key.

Die Entwickler-Dokumentation gibt es nur auf Englisch, wie die API selbst.

Simple Feedback speaks the Model Context Protocol (MCP). Connect an AI assistant to it and the assistant can list your venues, build and publish surveys, create QR codes and read responses, statistics and insights. It runs on your assistant and your model; the only call that uses your plan's AI quota is generating insights.

The MCP server uses the same scopes and plan check as the REST API. An assistant connects either by signing in (OAuth, nothing to copy) or with an API key; see Authentication and API keys for keys.

Endpoint

Setting Value
URL https://simplefeedback.de/api/mcp
Transport Streamable HTTP, stateless
Method POST only; GET and DELETE answer 405
Auth Authorization: Bearer <token>: an OAuth access token or an API key
Headers Content-Type: application/json and Accept: application/json, text/event-stream

There is no MCP session: every request is authenticated on its own.

Sign in from the assistant (OAuth)

Clients that support MCP authorization, such as Claude and Claude Code, need only the URL. On the first connection the client opens a Simple Feedback page in your browser. Sign in, pick the organization, untick any permission you don't want to give, and allow access. An owner or admin of the organization has to do this, and the organization's plan must include API access.

The client receives a token that lasts an hour and refreshes it on its own. The access shows up under Settings → API keys → Connected apps with the app's name and who approved it. There you can change what the app may do, which applies from its next request, or disconnect it, which signs the client out.

For your own client, the server follows the MCP authorization spec: a request without a token answers 401 with a WWW-Authenticate header pointing at https://simplefeedback.de/api/.well-known/oauth-protected-resource/mcp. Registration is dynamic (RFC 7591), the code flow requires PKCE with S256, and refresh tokens rotate on every use. A refresh token used twice ends the grant. Scopes are the API scopes, space-separated; a client that asks for none is offered all of them.

Connect a client

Claude

In Claude, add a custom connector with the URL https://simplefeedback.de/api/mcp and connect it. Claude opens the sign-in page.

Claude Code

bash
claude mcp add --transport http simplefeedback "https://simplefeedback.de/api/mcp"

Then run /mcp in Claude Code and choose to authenticate. With an API key instead, add --header "Authorization: Bearer $SF_KEY" to the command.

Any client that supports remote MCP servers

Enter the URL https://simplefeedback.de/api/mcp. If the client can't sign in, add the header Authorization: Bearer <your API key>.

Clients that only start local servers

Some desktop clients can only launch local (stdio) servers from a config file. Bridge them with the community package mcp-remote:

json
{
  "mcpServers": {
    "simplefeedback": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://simplefeedback.de/api/mcp", "--header", "Authorization: Bearer sfk_live_..."]
    }
  }
}

If your client can add a remote server directly, prefer that. It is one moving part fewer.

Your own agent in TypeScript

typescript
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const transport = new StreamableHTTPClientTransport(new URL('https://simplefeedback.de/api/mcp'), {
  requestInit: { headers: { Authorization: `Bearer ${process.env.SF_KEY}` } },
});

const client = new Client({ name: 'my-integration', version: '1.0.0' });
await client.connect(transport);

const { tools } = await client.listTools();
const me = await client.callTool({ name: 'whoami', arguments: {} });

Check the connection with curl

bash
curl -X POST "$SF_API/mcp" \
  -H "Authorization: Bearer $SF_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

The answer lists the tools your key may use.

Tools

Tool Scope What it does
about_simplefeedback none What Simple Feedback is and what your plan allows
whoami none The organization, plan, scopes and venue of the key
list_venues surveys:read All venues of the organization
list_surveys surveys:read Survey summaries, newest first, paginated
get_survey surveys:read One survey with its pages, questions and settings
create_survey surveys:write Creates a survey, from a full body or from a short draft
update_survey surveys:write Changes a survey; each field sent replaces the old value
delete_survey surveys:write Deletes a survey for good
undo_last_change surveys:write Puts a survey back the way it was before the last change made through the API
get_survey_qr surveys:read The public link and a QR code image of it
get_plan_fit surveys:read Surveys beyond the plan, the locked ones and when the grace period ends
preview_plan_copy surveys:read What a copy that fits the plan would contain and lose
create_plan_copy surveys:write Replaces a survey with a copy that fits; moving QR codes also needs qr:write
create_qr_code qr:write A saved QR code with a short link and scan statistics
list_qr_codes qr:read Saved QR codes, newest first, paginated
get_qr_code qr:read One saved QR code, optionally with its image
get_qr_code_stats qr:read Scans per day and platform, and how many ended in a response
get_qr_codes_stats qr:read All codes compared: scans, responses, busy hours and hints
update_qr_code qr:write Renames a code, switches it off or points it at another survey
delete_qr_code qr:write Deletes a saved QR code and its scan statistics for good
create_access_codes access-codes:write Creates single-use codes; they are shown only in this result
list_access_codes access-codes:read Single-use codes with their status, newest first, paginated
revoke_access_codes access-codes:write Revokes the listed codes or every unused one
get_access_code_qr access-codes:read The link and a QR code image of one code you hold
list_responses responses:read Submitted responses, newest first, paginated
get_survey_statistics responses:read Counts, distributions and averages per question
get_insights insights:read The stored AI analysis, without generating anything
generate_insights insights:write Runs a new AI analysis; the only tool that uses your AI quota
upload_image files:write Uploads a survey logo or background; returns the id a survey refers to

The tool list follows the key's scopes. A read-only key never sees create_survey, so the assistant only plans with tools that will work. The scope is still checked on every call.

Hints for the client. Every tool carries MCP annotations. Tools that only read are marked readOnlyHint, delete_survey, delete_qr_code and revoke_access_codes are marked destructiveHint, and the other writing tools are neither. The tools behind a POST route (create_survey, undo_last_change, create_qr_code, create_plan_copy, create_access_codes, revoke_access_codes, get_access_code_qr, generate_insights) are also marked idempotentHint, because they take an idempotencyKey. Clients such as Claude use the hints to decide which calls to confirm with you first. They are hints only: the key's scopes decide what a call may do.

Retries. The same tools take an optional idempotencyKey, which works like the REST Idempotency-Key header: repeat a call with the same key after a timeout, and you get the first result instead of a second survey, a second QR code, a second batch of codes or a second AI run.

Drafts. For a new survey, create_survey takes a short draft: a name, a language, a welcome line and a flat list of questions. The server builds the pages, ids and presentation defaults from it, exactly as for POST /v1/surveys.

Undo. Every change made through the API or MCP can be undone one step at a time with undo_last_change, or with POST /v1/surveys/{id}/undo over REST. It answers NOTHING_TO_UNDO when there is no such change, which includes edits made in the dashboard, and SURVEY_CHANGED_SINCE when someone edited the survey afterwards. It doesn't bring back a deleted survey.

QR codes. get_survey_qr returns a throwaway image that is recorded nowhere. Pass includeImage: false when the link is all you need; the PNG is tens of kilobytes of context. create_qr_code creates a code that shows up in the dashboard, has a short link and counts scans; it counts against your plan's QR code limit.

Single-use codes. For a survey that each person may answer only once with a personal code, the assistant sets settings.participation.accessMode to code with update_survey and creates the codes with create_access_codes. The codes are in that result only, so it hands them to you right away. See Single-use codes.

After a downgrade. Surveys that use more than the new plan allows (more pages or languages, custom branding, a Google review link) keep working until the grace period ends; then they are locked and respondents see a pause page. get_plan_fit lists them with what each one goes beyond. preview_plan_copy shows the copy that would fit, and create_plan_copy creates it, moves the QR codes you list in qrCodeIds to it and locks the original for good. The copy starts without responses; the original's stay readable. List the survey's codes with list_qr_codes first: a code you leave out keeps opening the locked original. Over REST the same is GET /v1/surveys/plan-fit and GET / POST /v1/surveys/{id}/plan-copy.

Resources

The server publishes four resources. The schemas describe the full survey body that get_survey returns and update_survey accepts; a body that matches them is a body the server accepts.

URI Type Content
simplefeedback://schema/survey-modules application/schema+json JSON Schema of a survey's pages and every question type
simplefeedback://schema/survey-settings application/schema+json JSON Schema of the survey settings
simplefeedback://schema/survey-branding application/schema+json JSON Schema of the custom header and footer
simplefeedback://guide/getting-started text/markdown A short walkthrough written for the model

Errors

A failed tool call is a normal result marked isError. Its text starts with the same CODE: message the REST API uses, followed by details as JSON when there are any:

json
{
  "isError": true,
  "content": [{ "type": "text", "text": "SURVEY_NOT_FOUND: Survey not found" }]
}

The codes are listed in Errors. What an assistant should do with the common ones:

Code What to do
INSUFFICIENT_SCOPE Don't retry; tell the user which scope the key needs
VALIDATION_FAILED Read the schema resource again and fix the input
SURVEY_NOT_FOUND Wrong id, or another organization's survey
EMAIL_NOT_VERIFIED Don't retry; the user has to verify their email
TIER_LIMIT_REACHED, FEATURE_NOT_IN_TIER, AI_QUOTA_EXCEEDED Don't retry; a person has to upgrade or wait
SURVEY_CHANGED_SINCE Read the survey and make the change the user wants
RATE_LIMITED Wait and retry
UPSTREAM_ERROR, INTERNAL_ERROR Retry later

When create_survey with active: true creates the survey but can't publish it, details.createdSurveyId names the draft. Publish it with update_survey once the problem is fixed instead of creating it again.

A rejected key fails before any tool runs. It answers with HTTP 401 or 403 and a JSON-RPC error with code -32001:

json
{
  "jsonrpc": "2.0",
  "id": null,
  "error": {
    "code": -32001,
    "message": "API_KEY_MISSING: Authorization header with a Bearer API key is required",
    "data": { "code": "API_KEY_MISSING" }
  }
}

Rate limits

MCP budgets are separate from the REST API's. Each JSON-RPC method has its own, and tools/call counts each tool separately:

Applies to Limit
Each key, per method or tool 300 requests per minute
Each key, get_survey_statistics 60 requests per minute
Each key, generate_insights 2 requests per minute
Each organization or IP address 300 requests per minute

Over the limit, the request answers HTTP 429 with a Retry-After header and a JSON-RPC error whose data carries retryAfterSeconds.

Batches. A JSON-RPC batch counts every message in it, and is refused as a whole before anything runs if it would go over a limit. A batch of more than 20 messages is rejected with HTTP 400 and data.maxBatchSize: 20; send fewer per request. Current MCP clients send one message per request and never meet this.