An experimental localhost MCP endpoint that exposes Sidefy plugins, RSS, GitHub, rules, external events, and more as tools for MCP clients.
We’re adding an MCP Server to Sidefy — an experimental feature that exposes a local Model Context Protocol endpoint on your Mac. When enabled, MCP clients (such as Claude Desktop, Cursor, or Claude Code) can read and modify Sidefy configuration while the app is running.
Let AI Write Plugins and Rules for You
You no longer need to hand-write JavaScript plugins or SideScript rules from scratch. With MCP connected, describe what you want in plain language — the assistant can read Sidefy’s tool documentation, look up syntax references, and call create_custom_plugin or create_rule on your behalf.
Custom plugin example — tell your assistant something like:
Help me create a custom plugin that fetches today’s top stories from Hacker News and shows them on my timeline, one event per story with the title and link.
Event rule example — describe the filtering or styling you want:
I want to hide events whose title contains “newsletter” or “unsubscribe”, and highlight GitHub release events from
sidefy-team/sidefyin green.
The assistant will use tools such as list_custom_plugins, create_custom_plugin, validate_rule, preview_rule, list_rules, and create_rule to implement your request. Review the result in Sidefy before relying on it in daily use — especially for plugins that call external APIs or rules that hide or rewrite events.
Write Events to Your Timeline
With the External Events plugin, MCP clients can also create, update, and delete persisted events on your timeline — not only read Sidefy configuration. For example, ask your assistant to block focus time for this afternoon, or let a scheduled agent drop a daily digest after it finishes compiling headlines. See External Events: Let External Tools Put Any Event on Your Timeline → for examples.
How It Works
The MCP server runs inside Sidefy and listens on localhost only:
- Transport: Streamable HTTP (MCP)
- Endpoint:
http://127.0.0.1:<port>/mcp(default port39281, configurable in Experimental Features settings) - Authentication:
Authorization: Bearer <token>on every request - Requirement: Sidefy must stay running, and experimental features must stay enabled, while an MCP client is connected
To enable it, choose Enable Experimental Features from the Help menu, then open Experimental Features from the menu bar (under ADVANCED) and turn on MCP Server. Set Port if needed (valid range 1024–65535, default 39281), copy the token from the Connection section’s Authorization header example, and regenerate the access token from that panel.
Note: MCP Server is an experimental feature. Settings on the Experimental Features page are reset when experimental features are disabled, and the MCP server stops immediately.
Warning: AI or automated MCP clients may make incorrect changes. Review all actions after they run.
Client Setup
Before adding Sidefy in Cursor, Claude Code, or Claude Desktop:
- In the Help menu, turn on Enable Experimental Features and confirm the warning
- Open Experimental Features from the menu bar (under ADVANCED — this item appears only after step 1), enable MCP Server, set Port if needed, and confirm the status shows Healthy
- Keep Sidefy running and leave experimental features enabled while the MCP client is connected
- Copy
<token>from the Connection section’sAuthorization: Bearer …example; use the endpoint shown there for your client URL — update both if you change Port or regenerate the token
Cursor
Requires Cursor 0.48.0+ for Streamable HTTP. Open Cursor Settings → Tools & MCP, or edit ~/.cursor/mcp.json (project-level: .cursor/mcp.json):
{ "mcpServers": { "sidefy": { "url": "http://127.0.0.1:39281/mcp", "headers": { "Authorization": "Bearer <token>" } } }}If you changed Port in Sidefy, update the URL to match the endpoint shown in the Connection section (for example http://127.0.0.1:40000/mcp).
Claude Code
Add Sidefy to .mcp.json in your project root for team sharing, or to the top-level mcpServers in ~/.claude.json for all your projects:
{ "mcpServers": { "sidefy": { "type": "http", "url": "http://127.0.0.1:39281/mcp", "headers": { "Authorization": "Bearer <token>" } } }}Or use the CLI (defaults to local scope in ~/.claude.json; add --scope project to write .mcp.json instead):
claude mcp add --scope project --transport http sidefy http://127.0.0.1:39281/mcp \ --header "Authorization: Bearer <token>"Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{ "mcpServers": { "sidefy": { "url": "http://127.0.0.1:39281/mcp", "headers": { "Authorization": "Bearer <token>" } } }}Do not commit real tokens to git. Prefer environment variables in client configs when supported (for example Bearer ${env:SIDEFY_MCP_TOKEN} in Cursor).
Restart the MCP client after saving the config so the new server is picked up.
Tool Reference
The server exposes 45 tools. The Description column matches what MCP clients receive from tools/list. Arguments lists each tool’s input schema (required marked with *).
Event Source Plugins
| Tool | Description | Arguments |
|---|---|---|
list_plugins | List built-in Sidefy event source plugins and their enabled state. Custom JS plugins are listed separately via list_custom_plugins. | — |
set_plugin_enabled | Enable or disable a built-in plugin. Use list_plugins for valid plugin_id values; the aggregate CustomPlugins executor is not controllable via MCP — use list_custom_plugins and set_custom_plugin_enabled instead. | plugin_id (string) — Plugin identifier from list_plugins, e.g. com.sidecalendar.rssplugin; enabled (boolean) — Whether the plugin should be enabled |
RSS Feeds
| Tool | Description | Arguments |
|---|---|---|
list_rss_feeds | List configured RSS feeds | — |
add_rss_feed | Add an RSS feed subscription | url (string) — RSS feed URL; name (string) — Optional display name; category (string) — Optional category name; color (string) — Optional display color as hex, e.g. #FF5733; icon (string) — Optional icon image URL (http/https) |
remove_rss_feed | Remove an RSS feed subscription | url (string) — RSS feed URL to remove |
set_rss_feed_enabled | Enable or disable an RSS feed without removing it | url (string) — RSS feed URL; enabled (boolean) — Whether the feed should be enabled |
update_rss_feed | Update RSS feed display name, category, color, and/or icon. Pass color or icon as an empty string to clear custom values. | url (string) — RSS feed URL; name (string) — Optional display name; category (string) — Optional category name; color (string) — Optional hex color; empty string clears; icon (string) — Optional icon URL; empty string clears |
GitHub Repositories
| Tool | Description | Arguments |
|---|---|---|
list_github_repos | List configured GitHub repository subscriptions | — |
add_github_repo | Subscribe to a GitHub repository | repository (string) — Repository in owner/repo or GitHub URL form |
remove_github_repo | Remove a GitHub repository subscription | repository (string) — Repository in owner/repo form |
set_github_repo_enabled | Enable or disable a GitHub repository subscription | repository (string) — Repository in owner/repo form; enabled (boolean) — Whether the repository should be enabled |
update_github_repo | Update enabled GitHub event types for a repository. Valid types: stars, releases, pullRequests, forks, issues, discussions | repository (string) — Repository in owner/repo form; enabled_event_types (string[]) — GitHub event type identifiers to enable |
Event Rules (SideScript)
Rule tools append a documentation hint in their MCP description. Read the syntax guides before calling write tools:
| Tool | Description | Arguments |
|---|---|---|
list_rules | List SideScript event rules (id, name, enabled state). Before creating or editing rules, you MUST read the official SideScript syntax documentation yourself: SideScript Syntax Guide and SideScript 2.0 Template Syntax. Do not guess syntax—fetch and follow those docs. Pass a single rule body as SideScript text (WHEN … IF … THEN …). | — |
get_rule | Get one event rule including its SideScript source. (Same SideScript documentation hint as list_rules.) | rule_id (integer) — Rule database id |
create_rule | Create a SideScript event rule. (Same SideScript documentation hint as list_rules.) | sidescript (string) — Single SideScript rule body (WHEN … IF … THEN …); is_enabled (boolean) — Optional. Defaults to true. |
update_rule | Update an existing event rule from SideScript and/or enabled state. (Same SideScript documentation hint as list_rules.) | rule_id (integer) — Rule database id; sidescript (string) — Optional replacement SideScript rule body; is_enabled (boolean) — Optional enabled state |
set_rule_enabled | Enable or disable an event rule without changing its SideScript body | rule_id (integer) — Rule database id; enabled (boolean) — Whether the rule should be enabled |
delete_rule | Delete an event rule | rule_id (integer) — Rule database id |
validate_rule | Validate SideScript rule syntax and conditions without saving. Returns valid, error, and parsed name. (Same SideScript documentation hint as list_rules.) | sidescript (string) — Single SideScript rule body |
validate_rule_template | Validate SideScript rule template syntax without saving. Returns valid, error, and parsed name. (Same rule template documentation hint as list_rule_templates.) | sidescript (string) — SideScript template body (TEMPLATE NAME "..." IF ...) |
preview_rule | Simulate how a rule affects today’s timeline events without saving. Provide rule_id and/or sidescript. Uses today’s in-memory timeline after rules are applied; call refresh_events first if data may be stale. | rule_id (integer) — Optional existing rule id; sidescript (string) — Optional rule body (overrides saved body when both given); is_enabled (boolean) — Optional preview enabled state; include_existing_rules (boolean) — Optional. Default false previews only the test rule; source_id (string) — Optional source filter (same as list_today_events); limit (integer) — Optional max matches; default 200; include_unmatched (boolean) — Optional. Default false returns only matched events |
reorder_rules | Reorder all event rules by passing every rule id once in the desired execution order (matches list_rules sortOrder). | rule_ids (integer[]) — Ordered list of all rule database ids |
Rule Templates (SideScript)
Rule template tools append a documentation hint in their MCP description. Read the template syntax guide before calling write tools:
| Tool | Description | Arguments |
|---|---|---|
list_rule_templates | List SideScript rule templates (database id, templateId, name). Before creating or editing rule templates, you MUST read the official SideScript template syntax documentation yourself: SideScript 2.0 Template Syntax. For create_rule_template, pass template body without @id — Sidefy generates templateId automatically; the response includes it for use in rules. For update_rule_template, @id in sidescript is ignored; use template_id (database id) to identify the template. Pass SideScript text as TEMPLATE NAME "..." IF ... (import/get responses include TEMPLATE @id). Templates cannot nest other templates in their IF conditions. | — |
get_rule_template | Get one rule template including its SideScript source. (Same rule template documentation hint as list_rule_templates.) | template_id (integer) — Template database id from list_rule_templates |
create_rule_template | Create a SideScript rule template. (Same rule template documentation hint as list_rule_templates.) | sidescript (string) — SideScript template body without @id (TEMPLATE NAME "..." IF ...); templateId is generated by Sidefy |
update_rule_template | Update an existing rule template from SideScript. Use template_id (database id) to identify the template; @id in sidescript is ignored. (Same rule template documentation hint as list_rule_templates.) | template_id (integer) — Template database id from list_rule_templates; sidescript (string) — Replacement SideScript template body |
delete_rule_template | Delete a rule template | template_id (integer) — Template database id from list_rule_templates |
Custom JavaScript Plugins
Custom plugin tools append a documentation hint pointing to the Submit Plugin API reference and the official plugin repository (default sidefy-team/sidefy-plugins, configurable in Sidefy settings). fetchEvents(config) must be a synchronous function — not async, not a Promise. Use sidefy.http.get / sidefy.http.post for HTTP (blocking, returns response text or null). Dates must be full ISO 8601 with timezone.
| Tool | Description | Arguments |
|---|---|---|
list_custom_plugins | List user-defined custom JavaScript plugins. Each item includes eventSourceId for list_today_events filtering. (Custom plugin documentation hint in MCP description.) | — |
get_custom_plugin | Get one custom plugin including JavaScript source and config. (Same custom plugin documentation hint as list_custom_plugins.) | plugin_id (string) — Custom plugin UUID |
create_custom_plugin | Create a custom JavaScript plugin. Response includes setup_hints when js_code uses sidefy.ai.chat or sidefy.crawler. (Custom plugin constraints and documentation hint in MCP description.) | name (string) — Unique plugin name; js_code (string) — Synchronous fetchEvents(config) source; description (string) — Optional; version (string) — Optional semver x.y.z; defaults to 0.0.1; config (object) — Optional string key-value map; is_enabled (boolean) — Optional. Defaults to true. |
update_custom_plugin | Update an existing custom JavaScript plugin. Config merges by default (merge_config=true); set merge_config=false to replace the entire config map. (Same custom plugin documentation hint as list_custom_plugins.) | plugin_id (string) — Custom plugin UUID; name, description, version, js_code, config, merge_config, is_enabled — All optional |
set_custom_plugin_enabled | Enable or disable a custom plugin without changing its source code. (Same custom plugin documentation hint as list_custom_plugins.) | plugin_id (string) — Custom plugin UUID; enabled (boolean) — Whether the plugin should be enabled |
delete_custom_plugin | Delete a custom JavaScript plugin. (Same custom plugin documentation hint as list_custom_plugins.) | plugin_id (string) — Custom plugin UUID |
test_custom_plugin | Run custom JavaScript plugin code in an isolated engine without saving. Custom plugins only — online plugins are not supported. Provide plugin_id and/or inline js_code; optional config merges for this run only. Returns syntax check, logs, sample events, and setup hints. | plugin_id (string) — Optional UUID or name; js_code (string) — Optional inline source (overrides saved code when plugin_id is also given); name, version, config — Optional for inline tests |
Online Marketplace Plugins
Online plugin tools identify plugins only by directory_name from list_online_plugins (config-only MCP view: directoryName, isEnabled, config). Do not use test_custom_plugin or custom-plugin write tools for online plugins.
| Tool | Description | Arguments |
|---|---|---|
list_online_plugins | List downloaded online plugins (config-only: directoryName, isEnabled, config). (Online plugin documentation hint in MCP description.) | — |
get_online_plugin | Get one downloaded online plugin config snapshot. (Same online plugin documentation hint as list_online_plugins.) | directory_name (string) — Plugin repository directory name from list_online_plugins, e.g. switch_wishlist_discount |
update_online_plugin_config | Update a downloaded online plugin config and apply it immediately. Config merges by default (merge_config=true); set merge_config=false to replace the entire config map. (Same online plugin documentation hint as list_online_plugins.) | directory_name (string) — Plugin directory name; config (object) — String key-value config map; merge_config (boolean) — Optional. Default true |
set_online_plugin_enabled | Enable or disable a downloaded online plugin. (Same online plugin documentation hint as list_online_plugins.) | directory_name (string) — Plugin directory name; enabled (boolean) — Whether the plugin should be enabled |
Today’s Events
| Tool | Description | Arguments |
|---|---|---|
list_today_events | List today’s events currently shown on the Sidefy timeline (after rules are applied). Each event includes color and icon URL when available. Call refresh_events first if you need fresh data. Filter external events with source_id = com.sidecalendar.plugin.external. | source_id (string) — Optional filter: list_plugins.id, list_custom_plugins.eventSourceId, com.sidecalendar.plugin.external for external events, or CustomPlugins for all extension plugins; limit (integer) — Optional max events; default 200 |
External Events
External event tools append a documentation hint in their MCP description. See External Events: Let External Tools Put Any Event on Your Timeline → for usage examples. Writable tools trigger a timeline refresh; use list_today_events with source_id = com.sidecalendar.plugin.external to verify display.
| Tool | Description | Arguments |
|---|---|---|
list_external_events | List persisted external events stored by the External Events plugin. Results sorted by startDate ascending. (External Events documentation hint in MCP description.) | channel (string) — Optional channel filter, e.g. mcp; start_date / end_date (string) — Optional ISO 8601 range for overlap filtering (end_date requires start_date); limit (integer) — Optional max events; default 200 |
get_external_event | Get one persisted external event by id. (Same External Events documentation hint.) | id (string) — External event id |
create_external_event | Create a persisted external event. Fails if id already exists. (Same External Events documentation hint.) | title, startDate, endDate, color, isAllDay, isPointInTime; optional id, channel, notes, icon, href, imageURL, eventType |
update_external_event | Fully replace an existing external event by id. (Same External Events documentation hint.) | id plus the same fields as create_external_event |
delete_external_event | Delete a persisted external event by id. (Same External Events documentation hint.) | id (string) — External event id |
Refresh
| Tool | Description | Arguments |
|---|---|---|
refresh_events | Trigger a manual refresh of all event sources | — |
Read-only / non-refresh tools — list_*, get_*, validate_rule, validate_rule_template, preview_rule, test_custom_plugin, list_today_events, list_external_events, and get_external_event do not trigger a post-call timeline refresh. update_online_plugin_config applies config immediately but also skips the coalesced refresh path. All other write tools — including enable/disable toggles and external event create/update/delete — apply changes and then run the MCP refresh path (clear event ID cache and request a full timeline refresh). You do not need to call refresh_events after a successful write unless you want to refresh without changing configuration.
Example Workflows
With MCP connected, an assistant can:
- Add an RSS feed with custom color and icon, then confirm it in
list_rss_feeds - Toggle a feed with
set_rss_feed_enabledor update its label viaupdate_rss_feed - Subscribe to a GitHub repo and configure event types with
update_github_repo - Validate SideScript with
validate_rule, preview impact withpreview_rule, then create it withcreate_rule - Reorder rules with
reorder_rulesafter listing ids vialist_rules - Create a reusable rule template with
create_rule_template, then reference it from new rules - Dry-run custom plugin code with
test_custom_pluginbeforecreate_custom_pluginorupdate_custom_plugin - Inspect today’s timeline with
list_today_events, filtered bysource_idwhen needed - Block focus time or drop a briefing with
create_external_event, then confirm on the timeline vialist_today_eventswithsource_id=com.sidecalendar.plugin.external - List or edit persisted external events with
list_external_events,update_external_event, ordelete_external_event - Patch a downloaded plugin’s API keys via
update_online_plugin_config(merge_config=trueby default; usedirectory_namefromlist_online_plugins) - Enable or disable a built-in plugin with
set_plugin_enabled
All changes go through the same validation and persistence layers as the native UI.
Share Your Thoughts
We’d love to hear how you’d use MCP with Sidefy — which tools matter most, and what additional capabilities would help your workflow.
Feel free to share ideas and feedback through GitHub Issues.
MCP Server is coming in an upcoming release as an experimental feature in the Experimental Features settings panel.