SurfSense/plugins
DESKTOP-RTLN3BA\$punk ada244d4e2 docs(plugins): the host's Keygen settings, its redacted logs and the eight listed plugins
plugin-host.md describes the Keygen settings and answers, log redaction and the
new tests, and replaces the empty-account-id gaps with Reddit's and Instagram's.
.env.example gains the KEYGEN_* settings and names the eight plugins in PLUGINS.
2026-10-13 20:36:41 -07:00
..
bundles refactor(plugins): move the bundle tooling under plugins/bundles 2026-10-10 20:19:47 +05:30
registry fix(registry): list DeepWiki as Community 2026-10-13 20:36:41 -07:00
remote docs(plugins): the host's Keygen settings, its redacted logs and the eight listed plugins 2026-10-13 20:36:41 -07:00
README.md feat(plugins): the third-party plugin registry, its check, signing and container 2026-10-12 15:59:55 +05:30

Plugins

A plugin is a remote MCP server that gives SurfSense's agent tools. You host the server; SurfSense lists it, asks the user before connecting, and calls it. Any MCP server that works in other MCP clients works here unchanged. The design is the plugins proposal.

Folder What it holds
registry/ The list of third-party plugins, their icons, the check every entry passes, and the container that serves them
remote/ SurfSense's own plugins and the plugin host that runs them. Maintainers only for remote/proprietary/
bundles/ Deferred. The earlier, local design's SDK and tooling, kept for plugins that run on the user's machine

The rest of this page is for publishers: companies and developers listing their own MCP server.

1. Build a server

  • Remote, over Streamable HTTP, on HTTPS. SurfSense does not run servers started by a command (npx, uvx), and does not speak the legacy SSE transport. Any official MCP SDK serves Streamable HTTP.

  • Protocol 2025-11-25 or newer. SurfSense also speaks 2026-07-28.

  • Stateless if you can. A server that keeps no session between requests survives restarts and scaling, and loses nothing when SurfSense reconnects it, as it does when a sign-in token is refreshed.

  • Annotate every tool. readOnlyHint and destructiveHint decide whether SurfSense asks the user before a call. A tool that says neither may destroy, as MCP's defaults say, so it asks before every call; a read-only one, or one marked destructiveHint: false, asks until the user chooses to always allow it.

  • Describe tools for a model. Say what the tool does and when to use it, as briefly as that allows: every description reaches the model on every turn. Instructions aimed at the model, such as "always call this first", are rejected in review.

  • Sign-in, one of:

    auth The user Your server receives
    none Does nothing No credentials
    token Pastes a key you give them Authorization: Bearer <key>
    oauth Signs in on your page in their browser Authorization: Bearer <access token>

    OAuth follows MCP's authorization spec: serve protected resource metadata, and support Client ID Metadata Documents or dynamic client registration. SurfSense signs in as https://plugins.surfsense.com/oauth/client.json and redirects to https://plugins.surfsense.com/oauth/callback. If you only accept clients registered in advance, register SurfSense with those values and put the client id in your entry's auth.client_id. SurfSense is a public client: it never holds a client secret.

2. Try it in SurfSense

You need no listing to try your server:

  1. Settings → Plugins → turn on Allow third-party plugins.
  2. Add MCP server, with its URL and, if it needs one, a key.
  3. Allow the tools you want, then ask the agent to use them in an Agentic thread.

A server added this way is marked third-party, not reviewed by SurfSense, and stays on that one computer.

3. List it

Open a pull request into dev that adds your entry to registry/plugins.json and your icon to registry/icons/.

{
  "id": "acme-docs",
  "kind": "remote",
  "publisher": "community",
  "name": "Acme Docs",
  "description": "Search and read Acme's public documentation.",
  "author": "Acme Inc.",
  "url": "https://mcp.acme.example/mcp",
  "auth": { "type": "none" },
  "hosts": ["mcp.acme.example"],
  "access": "free",
  "privacy_policy": "https://acme.example/privacy",
  "homepage": "https://acme.example",
  "icon": "acme-docs.svg"
}
  • id: lowercase letters, digits and hyphens, unique, never reused once listed.
  • publisher: community. partner (shown as "Verified") needs evidence in the pull request that you speak for the company that runs the server.
  • hosts: every host SurfSense contacts for your plugin: your server's, and for OAuth your authorization server's. The user consents to each.
  • access: free, or external with an access_note saying what the user must buy or have.
  • privacy_policy: required. The user's requests and credentials reach your server, so this is yours, not your API provider's.
  • icon: SVG or PNG, square, at most 32 KB; PNG at least 64×64; SVG with no scripts, event handlers or external references. Without one, SurfSense shows your name's first letter.

The full rules are in the registry proposal.

What happens to your pull request

  1. CI checks the entry against the rules above, and your icon.
  2. A maintainer reviews it once: they connect your plugin, signing in if it needs it, and check who runs the server, what its tools do, that no description carries instructions aimed at the model, that hosts is complete, and that the privacy policy is real.
  3. Once merged into dev, CI signs the list. It is published at plugins.surfsense.com/plugins.json with SurfSense's next release from dev to main, and reaches every app that allows third-party plugins with no app update.

To run the same check yourself, from plugins/registry/check/:

uv sync
uv run python -m registry_check static   # the entry rules and your icon

4. After it is listed

  • Changing your server needs no pull request. Deploy when you like. A tool you add, or one whose description or annotations change, starts off in every app until the user turns it on.
  • Changing your entry is another pull request and another review.
  • Removing a plugin moves its entry to removed with a reason. Apps stop offering its tools and show the reason; users keep their connection in case it returns.