Skip to content

BLOG

MCP Server: Let AI Assistants Manage Sidefy

FeatureMCPExperimental

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/sidefy in 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 port 39281, 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 102465535, 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:

  1. In the Help menu, turn on Enable Experimental Features and confirm the warning
  2. 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
  3. Keep Sidefy running and leave experimental features enabled while the MCP client is connected
  4. Copy <token> from the Connection section’s Authorization: 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):

Terminal window
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

ToolDescriptionArguments
list_pluginsList built-in Sidefy event source plugins and their enabled state. Custom JS plugins are listed separately via list_custom_plugins.
set_plugin_enabledEnable 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

ToolDescriptionArguments
list_rss_feedsList configured RSS feeds
add_rss_feedAdd an RSS feed subscriptionurl (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_feedRemove an RSS feed subscriptionurl (string) — RSS feed URL to remove
set_rss_feed_enabledEnable or disable an RSS feed without removing iturl (string) — RSS feed URL; enabled (boolean) — Whether the feed should be enabled
update_rss_feedUpdate 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

ToolDescriptionArguments
list_github_reposList configured GitHub repository subscriptions
add_github_repoSubscribe to a GitHub repositoryrepository (string) — Repository in owner/repo or GitHub URL form
remove_github_repoRemove a GitHub repository subscriptionrepository (string) — Repository in owner/repo form
set_github_repo_enabledEnable or disable a GitHub repository subscriptionrepository (string) — Repository in owner/repo form; enabled (boolean) — Whether the repository should be enabled
update_github_repoUpdate enabled GitHub event types for a repository. Valid types: stars, releases, pullRequests, forks, issues, discussionsrepository (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:

ToolDescriptionArguments
list_rulesList 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_ruleGet one event rule including its SideScript source. (Same SideScript documentation hint as list_rules.)rule_id (integer) — Rule database id
create_ruleCreate 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_ruleUpdate 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_enabledEnable or disable an event rule without changing its SideScript bodyrule_id (integer) — Rule database id; enabled (boolean) — Whether the rule should be enabled
delete_ruleDelete an event rulerule_id (integer) — Rule database id
validate_ruleValidate 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_templateValidate 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_ruleSimulate 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_rulesReorder 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:

ToolDescriptionArguments
list_rule_templatesList 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_templateGet 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_templateCreate 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_templateUpdate 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_templateDelete a rule templatetemplate_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.

ToolDescriptionArguments
list_custom_pluginsList user-defined custom JavaScript plugins. Each item includes eventSourceId for list_today_events filtering. (Custom plugin documentation hint in MCP description.)
get_custom_pluginGet 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_pluginCreate 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_pluginUpdate 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_enabledEnable 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_pluginDelete a custom JavaScript plugin. (Same custom plugin documentation hint as list_custom_plugins.)plugin_id (string) — Custom plugin UUID
test_custom_pluginRun 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.

ToolDescriptionArguments
list_online_pluginsList downloaded online plugins (config-only: directoryName, isEnabled, config). (Online plugin documentation hint in MCP description.)
get_online_pluginGet 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_configUpdate 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_enabledEnable 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

ToolDescriptionArguments
list_today_eventsList 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.

ToolDescriptionArguments
list_external_eventsList 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_eventGet one persisted external event by id. (Same External Events documentation hint.)id (string) — External event id
create_external_eventCreate 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_eventFully replace an existing external event by id. (Same External Events documentation hint.)id plus the same fields as create_external_event
delete_external_eventDelete a persisted external event by id. (Same External Events documentation hint.)id (string) — External event id

Refresh

ToolDescriptionArguments
refresh_eventsTrigger a manual refresh of all event sources

Read-only / non-refresh toolslist_*, 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_enabled or update its label via update_rss_feed
  • Subscribe to a GitHub repo and configure event types with update_github_repo
  • Validate SideScript with validate_rule, preview impact with preview_rule, then create it with create_rule
  • Reorder rules with reorder_rules after listing ids via list_rules
  • Create a reusable rule template with create_rule_template, then reference it from new rules
  • Dry-run custom plugin code with test_custom_plugin before create_custom_plugin or update_custom_plugin
  • Inspect today’s timeline with list_today_events, filtered by source_id when needed
  • Block focus time or drop a briefing with create_external_event, then confirm on the timeline via list_today_events with source_id = com.sidecalendar.plugin.external
  • List or edit persisted external events with list_external_events, update_external_event, or delete_external_event
  • Patch a downloaded plugin’s API keys via update_online_plugin_config (merge_config=true by default; use directory_name from list_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.