Koskamo

sync.day MCP

A public MCP server that allows AI assistants to create group scheduling events, parse event text, read event details, submit availability, vote on times, decide on a final time, and manage participants without requiring user accounts or API keys.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

sync.day MCP logo, by sync.dayCommunity listingStreamable HTTPNo sign-inEndpoint checked 2026-11-06Published by sync.day
MCP endpoint (Streamable HTTP)
https://sync.day/mcp/mcp
Vendor documentation

What its tools do

8 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.

8 of 8 have a description · 8 of 8 declare behavior hints · 8 of 8 with parameters document them all

Checks and signals

Each line is separate evidence, not a trust score.

  • Passed Endpoint answered the live check ·
  • Availability 1 of 1 checks passed in 30 days

What you can do with it

  • Create a group scheduling event and get a share link
  • Parse natural language text into event creation inputs
  • Submit individual availability for a scheduled event
  • Decide on the final meeting time as an organizer

Source and maintenance

Community listingRegistry 1.0.0

Protocol and capabilities

Server
sync.day 1.0.0
MCP protocol negotiated in check
2025-11-25
Tools
8 advertised
Resources
Not advertised
Resource templates
Not advertised
Prompts
Not advertised
Server-advertised capabilities
{
  "tools": {
    "listChanged": true
  }
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

sync.day (约时间) collects availability for a fixed-length event, like When2meet. Flow: create_event (or parse_event_text first to turn a sentence into settings) -> share the returned url -> each person calls submit_availability with the start keys they can make -> get_event shows the ranked starts. Slot keys are candidate START times in the form YYYY-MM-DDTHH:MM (naive local time, no timezone). Once 2+ people answered, get_event.voteOptions lists the starts EVERYONE can make; people approve any number of them with vote_time, and the organiser settles it with decide_time (which locks picking and voting until decided with start=null). Identity: submit_availability and vote_time take an `identity` string: a random secret of at least 16 chars that you generate and keep per person (e.g. 32 hex chars) — never a plain user id or name, because anyone who guesses it can act as that person on every event. The server stores only its hash; the same identity later updates the same row, votes need an identity that already submitted at least one start, and names are display only (they may repeat). Keep the adminKey/adminUrl private: it is needed for update_event, remove_participant and decide_time.

Tools

8 advertised

create_event — Create event
Advertised by the server during the last successful anonymous check.

Create a sync.day availability poll and get its share link. Candidate days run from startDate to endDate (inclusive; or give days instead). Each day offers candidate START times from hourFrom, every startStepMinutes, as long as start + durationMinutes <= hourTo. Returns slug, public url (share this), adminUrl and adminKey (keep private; needed for update_event / remove_participant). Example: 周六周日下午聚餐 3 小时 -> {title:'周末聚餐', startDate:'2026-09-05', endDate:'2026-09-06', hourFrom:13, hourTo:18, durationMinutes:180, startStepMinutes:60}.

create_event input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "title",
    "startDate",
    "hourFrom",
    "hourTo",
    "durationMinutes"
  ],
  "properties": {
    "days": {
      "type": "integer",
      "maximum": 21,
      "minimum": 1,
      "description": "Number of consecutive days from startDate (1-21); alternative to endDate."
    },
    "title": {
      "type": "string",
      "maxLength": 60,
      "minLength": 1,
      "description": "Event name, max 60 chars. e.g. 周末聚餐, Team sync"
    },
    "hourTo": {
      "type": "integer",
      "maximum": 24,
      "minimum": 1,
      "description": "End of each day's window, exclusive (1-24), must be > hourFrom. e.g. 22"
    },
    "endDate": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Last candidate day, inclusive, YYYY-MM-DD (same as startDate for a single day; at most 21 days). Give this or days; endDate wins when both are set."
    },
    "hourFrom": {
      "type": "integer",
      "maximum": 23,
      "minimum": 0,
      "description": "Earliest hour of each day (0-23). e.g. 9"
    },
    "question": {
      "type": "string",
      "maxLength": 200,
      "description": "Optional prompt shown to participants, max 200 chars. e.g. 哪天晚上有空?"
    },
    "startDate": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "First candidate day, YYYY-MM-DD. e.g. 2026-09-05"
    },
    "durationMinutes": {
      "anyOf": [
        {
          "type": "number",
          "const": 30
        },
        {
          "type": "number",
          "const": 60
        },
        {
          "type": "number",
          "const": 90
        },
        {
          "type": "number",
          "const": 120
        },
        {
          "type": "number",
          "const": 180
        },
        {
          "type": "number",
          "const": 240
        },
        {
          "type": "number",
          "const": 480
        }
      ],
      "description": "Event length in minutes. One of 30, 60, 90, 120, 180, 240, 480."
    },
    "startStepMinutes": {
      "anyOf": [
        {
          "type": "number",
          "const": 30
        },
        {
          "type": "number",
          "const": 60
        }
      ],
      "default": 60,
      "description": "Gap between candidate start times in minutes: 30 or 60 (default 60)."
    }
  }
}
create_event annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": false,
  "destructiveHint": false
}
parse_event_text — Parse event text
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Turn a natural-language sentence (Chinese works best) into create_event input using sync.day's AI parser. Does NOT create anything; review the result, then call create_event. Example: '下周六或周日下午聚餐,大概 3 小时'.

parse_event_text input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "text"
  ],
  "properties": {
    "text": {
      "type": "string",
      "maxLength": 300,
      "minLength": 1,
      "description": "One sentence describing the event, max 300 chars."
    },
    "today": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Reference date for relative words like 明天/下周, YYYY-MM-DD. Defaults to today in Asia/Shanghai."
    }
  }
}
parse_event_text annotations
{
  "readOnlyHint": true,
  "openWorldHint": true
}
get_event — Get event
Advertised by the server during the last successful anonymous check.
Advertised read-only

Read an event: settings, every candidate start key per day (startOptions), who has answered (participants with their start keys; names may repeat), the top 10 start times by head-count (ranked), the starts EVERYONE with picks can make with their votes (voteOptions), each person's vote (votes) and decidedStart. No key needed.

get_event input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "slug"
  ],
  "properties": {
    "slug": {
      "type": "string",
      "pattern": "^(?:[a-z]{3,7}-[a-z]{3,7}(?:-\\d{2}|-\\d{4})?|[A-Za-z0-9_-]{10})$",
      "description": "Event slug from the URL, e.g. calm-otter-4821 (sync.day/calm-otter-4821)"
    }
  }
}
get_event annotations
{
  "readOnlyHint": true,
  "openWorldHint": false
}
submit_availability — Submit availability
Advertised by the server during the last successful anonymous check.

Save (or replace) one person's availability: the candidate start keys they can make, from get_event.startOptions. The person is identified by `identity`, not by name: the same identity later overwrites the same row (and can rename it), a different identity with the same name is a second participant. Keys outside the event's start options are rejected. Returns the updated ranking.

submit_availability input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "slug",
    "identity",
    "name",
    "starts"
  ],
  "properties": {
    "name": {
      "type": "string",
      "maxLength": 20,
      "minLength": 1,
      "description": "Display name, 1-20 chars, may repeat. e.g. 小明"
    },
    "slug": {
      "type": "string",
      "pattern": "^(?:[a-z]{3,7}-[a-z]{3,7}(?:-\\d{2}|-\\d{4})?|[A-Za-z0-9_-]{10})$",
      "description": "Event slug from the URL, e.g. calm-otter-4821 (sync.day/calm-otter-4821)"
    },
    "starts": {
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$"
      },
      "maxItems": 1008,
      "description": "Start keys like 2026-09-05T14:00. Empty array clears this person's picks."
    },
    "identity": {
      "type": "string",
      "maxLength": 256,
      "minLength": 16,
      "description": "Random secret for this person that you generate and keep per person, 16-256 chars (e.g. 32 hex chars; NOT a plain user id or name, since anyone who guesses it can act as them). Same identity = same participant; the server stores only a hash. Example: 3f9a1c77b2e04d5e8a61f0c9d2b7e415"
    }
  }
}
submit_availability annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
remove_participant — Remove participant
Advertised by the server during the last successful anonymous check.
Advertised destructive

Organiser only: remove one participant by name (the oldest row with that name when it repeats), together with their vote. Requires the adminKey from create_event.

remove_participant input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "slug",
    "name",
    "adminKey"
  ],
  "properties": {
    "name": {
      "type": "string",
      "maxLength": 20,
      "minLength": 1,
      "description": "Exact participant name to remove."
    },
    "slug": {
      "type": "string",
      "pattern": "^(?:[a-z]{3,7}-[a-z]{3,7}(?:-\\d{2}|-\\d{4})?|[A-Za-z0-9_-]{10})$",
      "description": "Event slug from the URL, e.g. calm-otter-4821 (sync.day/calm-otter-4821)"
    },
    "adminKey": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{12}(?:[A-Za-z0-9_-]{12})?$",
      "description": "Organiser key returned by create_event (the ?key= part of adminUrl)."
    }
  }
}
remove_participant annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": true
}
update_event — Update event
Advertised by the server during the last successful anonymous check.

Organiser only: change any event settings (same fields and rules as create_event; omitted fields keep their current value; endDate wins over days). Existing picks are kept; those no longer valid starts are just hidden.

update_event input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "slug",
    "adminKey"
  ],
  "properties": {
    "days": {
      "type": "integer",
      "maximum": 21,
      "minimum": 1,
      "description": "Number of consecutive days from startDate (1-21); alternative to endDate."
    },
    "slug": {
      "type": "string",
      "pattern": "^(?:[a-z]{3,7}-[a-z]{3,7}(?:-\\d{2}|-\\d{4})?|[A-Za-z0-9_-]{10})$",
      "description": "Event slug from the URL, e.g. calm-otter-4821 (sync.day/calm-otter-4821)"
    },
    "title": {
      "type": "string",
      "maxLength": 60,
      "minLength": 1,
      "description": "Event name, max 60 chars. e.g. 周末聚餐, Team sync"
    },
    "hourTo": {
      "type": "integer",
      "maximum": 24,
      "minimum": 1,
      "description": "End of each day's window, exclusive (1-24), must be > hourFrom. e.g. 22"
    },
    "endDate": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Last candidate day, inclusive, YYYY-MM-DD (same as startDate for a single day; at most 21 days). Give this or days; endDate wins when both are set."
    },
    "adminKey": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{12}(?:[A-Za-z0-9_-]{12})?$",
      "description": "Organiser key returned by create_event (the ?key= part of adminUrl)."
    },
    "hourFrom": {
      "type": "integer",
      "maximum": 23,
      "minimum": 0,
      "description": "Earliest hour of each day (0-23). e.g. 9"
    },
    "question": {
      "type": "string",
      "maxLength": 200,
      "description": "Optional prompt shown to participants, max 200 chars. e.g. 哪天晚上有空?"
    },
    "startDate": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "First candidate day, YYYY-MM-DD. e.g. 2026-09-05"
    },
    "durationMinutes": {
      "anyOf": [
        {
          "type": "number",
          "const": 30
        },
        {
          "type": "number",
          "const": 60
        },
        {
          "type": "number",
          "const": 90
        },
        {
          "type": "number",
          "const": 120
        },
        {
          "type": "number",
          "const": 180
        },
        {
          "type": "number",
          "const": 240
        },
        {
          "type": "number",
          "const": 480
        }
      ],
      "description": "Event length in minutes. One of 30, 60, 90, 120, 180, 240, 480."
    },
    "startStepMinutes": {
      "anyOf": [
        {
          "type": "number",
          "const": 30
        },
        {
          "type": "number",
          "const": 60
        }
      ],
      "default": 60,
      "description": "Gap between candidate start times in minutes: 30 or 60 (default 60)."
    }
  }
}
update_event annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
vote_time — Approve start times
Advertised by the server during the last successful anonymous check.

Approval voting: set every start one person accepts (replaces their earlier approvals; an empty list clears them). The voter is the `identity` used in submit_availability and must have at least one start saved. Only keys listed in get_event.voteOptions are accepted; the error lists the current options otherwise. Rejected once the organiser decided. Example: {slug:'calm-otter-4821', identity:'3f9a1c77b2e04d5e8a61f0c9d2b7e415', starts:['2026-09-05T15:00','2026-09-06T10:00']}. `start` is shorthand for a single-item `starts`.

vote_time input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "slug",
    "identity"
  ],
  "properties": {
    "slug": {
      "type": "string",
      "pattern": "^(?:[a-z]{3,7}-[a-z]{3,7}(?:-\\d{2}|-\\d{4})?|[A-Za-z0-9_-]{10})$",
      "description": "Event slug from the URL, e.g. calm-otter-4821 (sync.day/calm-otter-4821)"
    },
    "start": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$",
      "description": "Shorthand for starts: [start]"
    },
    "starts": {
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$",
        "description": "Start key, e.g. 2026-09-05T15:00"
      },
      "maxItems": 500,
      "description": "All start keys this person accepts (from get_event.voteOptions); [] clears their approvals"
    },
    "identity": {
      "type": "string",
      "maxLength": 256,
      "minLength": 16,
      "description": "Random secret for this person that you generate and keep per person, 16-256 chars (e.g. 32 hex chars; NOT a plain user id or name, since anyone who guesses it can act as them). Same identity = same participant; the server stores only a hash. Example: 3f9a1c77b2e04d5e8a61f0c9d2b7e415"
    }
  }
}
vote_time annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
decide_time — Decide the time
Advertised by the server during the last successful anonymous check.

Organiser only: settle on a start time (locks availability picking and voting; the page shows 已定). Pass start=null to reopen. Requires the adminKey from create_event.

decide_time input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "slug",
    "adminKey",
    "start"
  ],
  "properties": {
    "slug": {
      "type": "string",
      "pattern": "^(?:[a-z]{3,7}-[a-z]{3,7}(?:-\\d{2}|-\\d{4})?|[A-Za-z0-9_-]{10})$",
      "description": "Event slug from the URL, e.g. calm-otter-4821 (sync.day/calm-otter-4821)"
    },
    "start": {
      "anyOf": [
        {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$"
        },
        {
          "type": "null"
        }
      ],
      "description": "A candidate start key of the event (normally one of voteOptions), or null to reopen."
    },
    "adminKey": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{12}(?:[A-Za-z0-9_-]{12})?$",
      "description": "Organiser key returned by create_event (the ?key= part of adminUrl)."
    }
  }
}
decide_time annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}

Prompts

Not advertised

No prompt definitions available.

Resources

Not advertised

No resource definitions available.

90-day uptime

A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.

Too few checks for a percentage yet (1 of 1 answered).

  1. 2026-11-07: 1 of 1 checks answered

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
797 ms
95th percentile latency
797 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 797 ms · metadata changed · 8 tool(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http sync-day-mcp https://sync.day/mcp/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add sync-day-mcp --url https://sync.day/mcp/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable sync-day-mcp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "sync-day-mcp": {
      "url": "https://sync.day/mcp/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "sync-day-mcp": {
      "type": "streamableHttp",
      "url": "https://sync.day/mcp/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start sync-day-mcp and inspect its tools.
VS Code configuration
{
  "servers": {
    "sync-day-mcp": {
      "type": "http",
      "url": "https://sync.day/mcp/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http sync-day-mcp https://sync.day/mcp/mcp

Claude Desktop

Open Claude Settings > Connectors > Add custom connector, enter this server URL and enable it for the conversation. This is a manual remote-connector handoff, not a desktop JSON configuration.
Claude Desktop configuration
https://sync.day/mcp/mcp

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "sync-day-mcp": {
      "serverUrl": "https://sync.day/mcp/mcp"
    }
  }
}

Let your agent find its tools

Find servers like sync.day MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.