Skip to content

feat: add MCP endpoint for sending messages - #1066

Merged
jmattheis merged 5 commits into
gotify:masterfrom
RedwindA:feat/mcp
Oct 10, 2026
Merged

jmattheis merged 5 commits into
gotify:masterfrom
RedwindA:feat/mcp

Conversation

@RedwindA

@RedwindA RedwindA commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Closes #1059

Adds an optional, built-in Model Context Protocol endpoint at /mcp, so AI agents can send messages with an application token:

claude mcp add gotify --transport http https://gotify.example.com/mcp --header "Authorization: Bearer <apptoken>"

Scope

Kept tightly scoped as discussed in #1059: sending messages only, nothing else.

  • Auth: application tokens only, via the existing RequireApplicationToken middleware (X-Gotify-Key, Authorization: Bearer, or ?token=). Client tokens and unknown tokens get 401, basic auth gets 403, so an agent can never read, delete or manage anything.

  • One tool: send_message with message (required), title, priority, and three convenience flags mapped to documented extras:

    • markdown → client::display.contentType
    • click_url → client::notification.click.url
    • big_image_url → client::notification.bigImageUrl

    Raw extras are intentionally not exposed, so models can't make up namespaces.

  • Stateless: Streamable HTTP in stateless + JSON-response mode. No sessions, no SSE, no extra server state; it behaves like any other REST endpoint behind a reverse proxy or with multiple instances.

  • Shared logic: the defaulting/store/notify part of CreateMessage is extracted into createMessage, so REST and MCP behave the same (default title = app name, default priority, stream notification).

  • Opt-out: GOTIFY_SERVER_MCP_ENABLED (default true). With false, /mcp is not registered at all.

Why built in rather than OpenAPI-to-MCP

  • Setup: an OpenAPI-to-MCP bridge is an extra service users have to host and maintain. Most AI clients only accept an MCP URL, and remote/hosted agents can't run a local bridge at all. Here it's one line of client config.
  • Better guidance than the spec: the tool schema carries hints the swagger spec doesn't, e.g. what priority ranges mean, and that click_url / big_image_url only take effect in the Android client.
  • Small maintenance surface: ~115 lines in api/mcp.go, one tool, reusing the existing message path.

Dependency

Uses the official github.com/modelcontextprotocol/go-sdk (v1.x, maintained by the MCP org with Google). It adds 5 small indirect modules: google/jsonschema-go, segmentio/encoding, segmentio/asm, yosida95/uritemplate, golang.org/x/time.

Tests

Integration tests in router/mcp_test.go use the SDK client against the real router: sending with and without extras, default title/priority, empty message, token types (app/client/unknown/basic auth), and the disabled switch.

Open question

The endpoint is enabled by default, since it doesn't grant anything an application token can't already do via POST /message. Happy to flip the default to false if you prefer.

@RedwindA
RedwindA requested a review from a team as a code owner October 5, 2026 13:26
Expose a stateless Streamable HTTP Model Context Protocol endpoint at
/mcp which provides a send_message tool to AI agents. The endpoint only
accepts application tokens and can be disabled with
GOTIFY_SERVER_MCP_ENABLED=false.
@codecov

codecov Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.29730% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.62%. Comparing base (f77d8a0) to head (3ae2545).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
api/message.go 80.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1066      +/-   ##
==========================================
+ Coverage   76.12%   76.62%   +0.50%     
==========================================
  Files          67       68       +1     
  Lines        3619     3688      +69     
==========================================
+ Hits         2755     2826      +71     
+ Misses        653      652       -1     
+ Partials      211      210       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread README.md Outdated
Comment thread README.md Outdated
Gotify provides a [Model Context Protocol](https://modelcontextprotocol.io/) endpoint at `/mcp` (Streamable HTTP, stateless),
which exposes a `send_message` tool to AI agents. Authenticate with an application token via the `Authorization: Bearer <token>` or
`X-Gotify-Key` header, or the `token` query parameter. The endpoint can be disabled with `GOTIFY_SERVER_MCP_ENABLED=false`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works great (:.

Prompt

Image

Output

Image

@RedwindA

Copy link
Copy Markdown
Contributor Author

Thanks for testing it out, glad it works well!

I've reverted the README changes and moved the docs to gotify/website#107, as a new "AI Agents (MCP)" page under Guides.

Comment thread gotify-server.env.example Outdated

@jmattheis jmattheis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@jmattheis
jmattheis added this pull request to the merge queue Oct 10, 2026
Merged via the queue into gotify:master with commit 2e18742 Oct 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: built-in MCP endpoint for sending messages from AI agents

2 participants