Skip to content

For agents

Connect your agent

Hackalendar speaks MCP. Point the agent you already use at it, and it can search the curated catalogue, follow new publications and important changes, or propose an event. Browsing is public. Personal subscriptions and contributions use private access.

Claude Code

claude mcp add --transport http hackalendar https://hackalendar.com/api/mcp

Cursor, Windsurf, or anything that reads mcp.json

{
  "mcpServers": {
    "hackalendar": {
      "url": "https://hackalendar.com/api/mcp"
    }
  }
}

Public tools

  • search_hackathons — Browse upcoming events — filter by text, city, theme or mode.query, city, theme (ai, web3, climate, health, data, hardware, student, creative), mode (in_person, online, hybrid), offset, limit (1–50, default 20). All optional. city matches the city label, case aside. Filtering by in_person or online also returns hybrid events. Follow nextOffset until null.
  • get_hackathon — One event in full: description, venue, dates, calendar link.slug, required — the value a search result hands back.
  • upcoming_deadlines — Registrations closing soon, soonest first.days (1–60, default 14), offset, limit (1–200, default 100). An event with no published deadline counts from its start date. Follow nextOffset until null.

Private access, without a required account

Open private access to create a management link. Save that link securely: it grants control of your private data. You can create scoped agent keys, rotate them and revoke access. No email is required. Losing the management link and browser session can leave management access unrecoverable.

MCP clients supporting the HTTP OAuth flow can open an authorization page when a private tool requires access. Other clients need support for a custom authorization header with a key you create in the management page. A key is not a URL or a tool argument:

Authorization: Bearer YOUR_API_KEY

Read calls remain available without credentials. Each private tool lists its required scope below. Grant only the scopes the agent needs. Your agent can inspect its own receipts and notifications; it cannot read another person’s inbox.

Private tools

  • submit_event — Propose an event URL and optional context for review. Returns a receipt; it does not publish. Reuse the same idempotency key when retrying.Scope: contributions:write
  • submit_feedback — Send a correction or suggestion for review, optionally about a public event. Never changes a listing directly.Scope: contributions:write
  • get_submission_status — Read your own contribution receipt and public listing link when published.Scope: contributions:read
  • create_subscription — Follow cities, modes and themes. OR within facets and between rules; AND across facets. Hybrid matches in-person and online. Returns current matches without an initial notification burst.Scope: subscriptions:write
  • list_subscriptions — List your saved event subscriptions.Scope: subscriptions:read
  • get_subscription — Read one subscription and its followed events. Preserve your existing inbox cursor; use get_notification_state for a complete resync.Scope: subscriptions:read
  • update_subscription — Replace one subscription. Criteria edits reset its followed set; active=false pauses it. Include all fields.Scope: subscriptions:write
  • delete_subscription — Delete one subscription and stop its future notifications.Scope: subscriptions:write
  • list_notifications — Read your private event notifications since a cursor. Save the returned cursor; follow hasMore. Includes publication, important changes, cancellation, withdrawal and restoration. History lasts 90 days.Scope: notifications:read
  • get_notification_state — Atomically read all events followed by your active subscriptions and a fresh inbox cursor. Replace your local state with this snapshot when initializing or recovering from cursor-expired, then poll list_notifications.Scope: notifications:read
  • register_webhook — Register an HTTPS endpoint. Returns a signing secret once; delivery starts only after verify_webhook succeeds.Scope: webhooks:write
  • verify_webhook — Send a signed challenge to your endpoint. It must return JSON with the exact challenge. Successful verification enables notification delivery.Scope: webhooks:write
  • list_webhooks — List your endpoints and delivery health without returning signing secrets.Scope: webhooks:read
  • update_webhook — Pause/resume an endpoint or rotate its signing secret. Rotation returns the new secret once and requires verification again.Scope: webhooks:write
  • delete_webhook — Remove an endpoint and its delivery history. Your subscriptions and private inbox remain.Scope: webhooks:write
  • list_webhook_deliveries — Inspect the latest 50 deliveries to one of your endpoints.Scope: webhooks:read
  • retry_webhook_delivery — Queue a failed delivery again using its original notification ID. The endpoint must be active.Scope: webhooks:write

Follow Paris or online events

Pass this to create_subscription. Cities use slugs here, such as paris. Values within a facet are OR; facets within one rule are AND; rules are OR. An empty facet accepts any value. Hybrid events match both online and in-person preferences.

{
  "name": "AI in Paris or online",
  "criteria": {
    "rules": [
      {
        "cities": [
          "paris"
        ],
        "modes": [
          "in_person"
        ],
        "themes": [
          "ai"
        ]
      },
      {
        "cities": [],
        "modes": [
          "online"
        ],
        "themes": [
          "ai"
        ]
      }
    ]
  }
}

Creating a subscription returns current matches and a cursor for that subscription. It does not send an initial burst of webhook alerts. Keep any existing inbox cursor when creating, editing or reading a subscription, so unread changes from other subscriptions remain reachable. For a first full sync, call get_notification_state, save its events and cursor, then call list_notifications and follow hasMore. Changes include dates, deadlines, venue, attendance, themes, entry details and registration links, plus cancellation and withdrawal. Cosmetic edits do not create alerts.

The inbox retains 90 days of history. An expired cursor requires get_notification_state: replace your local followed-event state with its authoritative snapshot, then use its cursor. This snapshot includes all active subscriptions at one boundary. An optional verified HTTPS webhook delivers notifications to your endpoint. A desktop agent without an endpoint must poll or be scheduled by its host; saving a subscription does not wake it automatically. Delivery is asynchronous, can be delayed and may repeat. Deduplicate notification IDs and apply event revisions in order.

Webhook event descriptions are limited to 1,000 characters; descriptionTruncated marks shortened text. The private inbox retains the full notification. If a notification still exceeds the webhook payload limit, that delivery fails without pausing the endpoint; retrieve it from the inbox. See the receiver contract for challenges and signatures.

Propose events and corrections

submit_event accepts url, optional context and contactEmail, and a required idempotencyKey. You do not need to be the organiser. Context is retained as a contribution, not proof of organiser identity.

submit_feedback accepts message, optional eventSlug, category and evidenceUrl, and the same required idempotency key. Both tools return a receipt. Reuse the key only to retry the same contribution; use a new key for different content. A received proposal is not published. A curator reviews it, and get_submission_status reports the result without exposing private notes.

Context and feedback are limited to 4,000 characters, URLs to 2,000 and JSON bodies to 64 KiB. Admission is limited to five contributions per hour per owner and network source, with a shared service cap. A quota response includes Retry-After. The anonymous submission form remains available too.

Try it before you wire it up

“Which AI hackathons are coming up in Paris?” is one call. No key, no auth, no session:

curl -s https://hackalendar.com/api/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"search_hackathons",
                 "arguments":{"city":"Paris","theme":"ai","limit":5}}}'

You get back a count and an events array. Every entry carries:

slug, name, url, startsAt, endsAt, timezone, temporalPrecision (when known), location, countryCode, mode, themes, isFree, prizePool, organizer, registrationUrl, registrationDeadline, deadlineNote, happeningNow, picked, pickedAt, pickNote, cancelled

Anything nobody could establish comes back as null rather than a guess, so an absent prizePool means we do not know it, not that there is none.

Is it up?

GET https://hackalendar.com/api/mcp/health

JSON, never cached. It answers 503 when the database is unreachable or health checks report degraded service, and 200 when status is ok. The response includes freshness checks, durable job health, the protocol version and the public/private tool inventory. It does not prove that a particular external webhook is reachable; inspect that endpoint’s delivery history for its status.

Publication stays curated. Contributions and feedback cannot publish or rewrite a listing. Signing up for a hackathon still happens on the organiser’s own page.