| admin.refresh_session_contextA | Re-read the session context (profile, upcoming events within 7d, conflicts, freshness nudge) as it stands NOW. Call this when the user just edited their profile or added/moved calendar events and you want the current picture instead of the snapshot taken at session start. Read-only; no side effects. Returns the same block Kontexta sent as MCP instructions at session start. |
| journal.writeA | Write one event to the current project's journal. kind: 'append' = timestamped entry in today's daily journal file in the Knowledge Base (creates the file if it doesn't exist; both calls on the same calendar day return the same file_id; returns {file_id}). kind: 'note' = free-form decision/abandonment/observation, stored as an agent_note event in Layer 1 (surfaces in distilled task entries; returns {ok, recorded_at}). kind: 'intent' = topic/intent pivot — use when the user redirects what you're working on so the distillation step splits task buckets correctly (returns {ok, recorded_at}). Required fields depend on kind: 'append'/'note' need text; 'intent' needs summary. |
| files.createA | Create one or more markdown, mermaid, or HTML files in the knowledge base or project (up to 200 per call). Pass a single-element files array for the one-file case. This operation writes each file to disk and adds it to the local SQLite FTS5 index. Destination can be 'knowledge' (global KB), 'project' (reference file inside a project repo), or 'kontexta' (internal Kontexta schema file). If destination is 'project' or 'kontexta', project_id is strictly required. If destination is 'knowledge', 'kind' is strictly required for md files — pick 'dictionary' (authoritative source-of-truth) or 'note' (informational snapshot); see the kind param for the rubric. No external auth required. Rate limits do not apply (local operation). Per-item failures are isolated to errors[] — the rest of the batch still commits; a single-item call still reports its failure the same way. Returns {created_count, error_count, created, errors}. If a destination directory does not exist, it will be created automatically. To modify an existing file, use 'files.update' instead. Pass format='mmd' on an item to create a Mermaid diagram file (.mmd) — for destination='knowledge' it's auto-routed to the KB's mermaid/ bucket (kind ignored, not required); for destination='project' it's written wherever folder says, same as any project file. Pass format='html' for an HTML report — destination MUST be 'knowledge' (html reports are auto-routed to the KB's html/ bucket; kind is ignored and not required for html). Format defaults to 'md'. |
| resources.add_reportA | Write an image or other binary resource into the shared reports/resources/ folder. Returns { filename, size, src, url } — embed the report's / tags with src exactly as given (e.g. <img src="resources/chart.png">); it is the only form that resolves correctly both in the dashboard viewer and in PDF/PNG export. Do not use url inside report HTML — it only works in the dashboard. Bytes are passed base64-encoded. |
| resources.list_reportsA | List all files currently stored under reports/resources/. Returns filename, size in bytes, and served URL. |
| resources.delete_reportA | Delete a file from reports/resources/. No-op if it doesn't exist. |
| resources.export_reportA | Export an existing HTML report as PDF or PNG. Returns { url } for the web-served download (requires an authenticated dashboard request) by default. Set inline_bytes=true to render in-process and get { bytes_base64 } instead — only available when running via the full kontexta CLI, not the standalone kontexta-mcp package; falls back to { url } with a note if unavailable. |
| files.readA | Read one or more files, in full or in part. Modes: single-by-id (id), single-by-path (path, absolute on-disk path — must be exactly as indexed), batch-by-id (ids, up to 200), partial-by-heading (id+section), partial-by-line-range (id+lines). Exactly one of id/path/ids is required. section and lines are mutually exclusive and only valid with id (not ids or path). Read-only; no side effects, auth, or rate limits. Response shape: a single file object (with content, tags, est_tokens) for id/path; a partial-content object for section/lines; {files, total_est_tokens, error_count, errors} for ids (per-ID failures isolated, batch never partial-throws). Prefer files.describe to inspect without paying body tokens. |
| files.describeA | Return everything ABOUT a file without pulling its content (no token cost from the body). Tags, size, est_tokens, history depth, related-file ids, backlinks, project, folder, last edited. Operates locally with no auth or rate limits. Use this when you'd otherwise chain files.read + tags.list + files.get_history + files.find_related just to decide whether to actually read the file. Parameters: 'id' must be a valid integer file ID. |
| files.regex_searchA | Match a JS regex against file bodies. Default mode scans every file in scope (project, KB, or all) and returns per-file hits with line numbers — slower than FTS files.search because it reads each file's content; use only when FTS misses substrings, URLs, or code identifiers. Pass file_id to instead scan just that one file (catches what FTS misses within a single known file); response shape changes to {file_id, path, pattern, match_count, truncated, matches}. Read-only; no side effects, auth, or rate limits. Multi-file mode capped at 500 files / 10 hits per file by default (files_truncated reports the cap); single-file mode capped at 100 hits by default, max 500. project_id/kind are ignored when file_id is set. Invalid regex throws. |
| files.updateA | Rewrite a file. Default = full-body replacement: content becomes the entire file, triggering disk write + FTS5 re-index. Pass section to instead rewrite ONLY that heading's body (case-insensitive exact-string after trim; the heading line itself is preserved, siblings untouched) — saves context budget vs resending the whole file. Throws if section is set but the heading doesn't exist (this mode will NOT create a new section — append the section text via a full-body update first). Operates locally with no external auth or rate limits. Returns the updated file metadata including new estimated token counts. |
| files.deleteA | DESTRUCTIVE. Permanently delete one or more files by ID (up to 500 per call). Pass a single-element ids array for the one-file case. KB files are unlinked from disk AND removed from the FTS5 index; project reference files only have their index entry removed (the file on disk is left alone so the watcher does not fight your editor). Not idempotent — deleting an unknown ID surfaces as a per-item error. No external auth or rate limits. Per-ID failures are isolated to errors[] and the rest of the batch still commits — partial success is the norm, always inspect error_count. Returns {deleted_count, error_count, deleted, errors}. Use only when the file is truly obsolete; to deprioritise without losing data, untag (tags.remove) or unfavorite (tags.set_favorite) instead. To preview the set before deleting, run files.list with the same filter and confirm the IDs. |
| files.listA | List file metadata with optional filters (project_id, tag, favorite, folder, untagged, kind) and pagination. Read-only; no side effects, auth, or rate limits. Each row is annotated with tags, est_tokens, size_bytes, and content_class; the response includes total_est_tokens so you can budget before reading bodies. project_id: null returns ONLY Knowledge Base files; omit the field to span everything; kind narrows to one content class. Use to browse known structure; for keyword/content lookup use files.search; for a denser whole-vault dump use projects.map. |
| files.searchA | Full-text (SQLite FTS5) keyword search across files. Default mode returns ranked matches with inline match_excerpt and title_highlight (no follow-up files.read needed for snippets) plus tags, est_tokens, size_bytes, content_class, and aggregate total_est_tokens. Pass include_bodies: true to instead get a single prompt-ready bundle: matched bodies concatenated into XML <document> blocks or markdown headers + fences (see format/max_tokens), capped at the token budget — files are added in rank order until the next would exceed it, the rest going to meta.skipped[]. Use include_bodies instead of files.search + N×files.read when you need several related files as one context blob. Read-only; no side effects, auth, or rate limits. Ordering: dictionary hits sort above everything else for the same query (dictionary-wins on conflict), then BM25 rank. FTS is tokenised: it WILL miss URLs, hyphenated terms, and partial substrings — fall back to files.regex_search for those. project_id: null searches only the KB; omit the field to span everything; tags[] requires ALL listed tags to match; kind narrows to one content class. |
| tags.addA | Append tags to ONE file. Additive — existing tags are preserved; re-adding an existing tag is a no-op (idempotent per tag). New tag names auto-create rows in the global tags table. Persists to local SQLite. No external auth or rate limits. Returns {success: true}; throws if file_id is unknown. Use to label a single file. To tag every file matching a query in one call use tags.search; to remove tags use tags.remove. |
| tags.removeA | Detach one or more tag IDs from ONE file. Destructive on the link only — does NOT delete the file or the global tag definition (orphan tags survive in tags.list). Idempotent: removing an already-absent tag is a no-op. No external auth or rate limits. Returns {success: true}. Note: takes tag IDs (integers), not names — fetch them via tags.list. To remove ALL tags from many files via a query, see tags.search (additive only) — there is no bulk-untag-by-query tool. |
| tags.set_favoriteA | Set or clear the favorite flag on one file (idempotent — re-setting the same value is a no-op; not a toggle, you pass the desired state). Persists to local SQLite. No external auth or rate limits. Returns {success: true}. Use to curate quick-access pins; files.list / files.search accept favorite: true to filter to the pinned set. |
| tags.listA | List every tag in the global SQLite database with id, name, and applied count. Read-only; no side effects, auth, or rate limits. Returns the entire taxonomy (not paginated). Use to discover existing labels before tagging (so you reuse rather than fork) or to find tag IDs to feed into tags.remove. For tags on a specific file, use files.describe. |
| projects.listA | List every registered project with id, name, absolute path, and a derived has_hands flag (true when the path exists on disk AND contains a kontexta.json). Read-only; no side effects, auth, or rate limits. Use to find the project_id to pass to scoped tools (files.search, files.list, admin.commit_backup, projects.refresh_index, etc.). To register a new project use projects.register; to inspect its Hands tools use hands.list. |
| projects.registerA | Register a new project and link it to the Kontexta knowledge system. SIDE EFFECTS: Writes project metadata to disk (persisted in the Kontexta data directory). Scans the project root recursively to discover and index all markdown files into the local database. Registers any kontexta.json-declared Hands tools found in the project root. This operation is idempotent — re-registering an existing project updates its metadata without data loss. AUTH / RATE LIMITS: None. Operates entirely on the local file system. PARAMETERS: name: Human-readable project name. path: Absolute path to the project root. Required. DO NOT guess or assume the path based on the active editor workspace unless the user explicitly asks to register the "current" or "open" project. If the user provides a project name but no path, ask them for the absolute path before calling this tool. Fails with a descriptive error if the path does not exist or is inaccessible. description: Optional free-text description stored with the project metadata.
RETURNS: A JSON object containing: project: { id, name, path, description, created_at } discovered_files_count: number of markdown files indexed discovered_files: array of { path, est_tokens, size_bytes } for each file total_est_tokens: estimated total token cost of all discovered files hands: { found, tools_registered, tools_disabled, warnings } warnings: array of non-fatal issues (e.g. scan failures, token budget exceeded)
ERROR CONDITIONS: Returns isError=true if path is missing or unresolvable. Scan failures are non-fatal and reported in warnings rather than as errors. |
| admin.onboard_agentA | Write or update the kontexta workflow rules block in a project's agent context file(s). Idempotent — uses fenced markers + version to skip no-op writes. MANDATORY: This tool modifies project configuration files. You MUST seek explicit user consent before calling this tool. Set 'confirm: true' only after the user has agreed. PARAMETERS: project_id: number, required. confirm: boolean, required. Must be true to proceed. files: string[], optional. Paths relative to project root. For update mode, defaults to recommendation.target_files. Ignored when files is empty AND target_agent is provided (create mode). target_agent: enum claude-code | codex | gemini | cursor | continue | aider | cline | copilot | generic. Required when files is empty AND no context file currently exists. Picks the canonical filename and the starter scaffold.
RETURNS: { written: [{ path, action: created|updated|skipped, version }], skipped: [{ path, reason }] } |
| admin.transfer_agent_contextA | COPY existing agent context files (CLAUDE.md, AGENTS.md, .cursor/rules/*.mdc, etc.) from a project's repo into Kontexta's per-project knowledge base so they're indexed by FTS5 and can be git-synced through Kontexta's own backup engine. This tool ONLY COPIES. It never deletes or modifies the originals in your repo. After a successful transfer, the response includes the list of source paths so the user can manually remove them if desired. No tool argument, no flag, and no code path in this tool ever calls a destructive filesystem operation against project.path. MANDATORY: This tool writes new files into Kontexta's data dir. You MUST seek explicit user consent before calling. Set 'confirm: true' only after the user has agreed. PARAMETERS: project_id: number, required. Project ID returned from register_project. confirm: boolean, required. Must be true. files: string[], optional. Project-relative paths to transfer. Omit or pass [] to transfer all detected agent context files (uses the same detection list as register_project / onboard_agent).
RETURNS: { transferred: [{ source_path, kb_id, kb_path, est_tokens }], skipped: [{ source_path, reason }], next_action }
Skip reasons: "missing" | "symlink" | "outside_project" | "already_transferred_same_content" | "read_error" | "write_error". IDEMPOTENT: re-running with the same files copies nothing if the content is unchanged — duplicate transfers are detected via SHA-256 hash comparison against existing project KB rows. |
| admin.commit_backupA | SIDE-EFFECTFUL — TOUCHES THE NETWORK. Sync the project's KB data into its git backup directory, create a commit, and git push to origin. AUTH: relies on the local user's git credentials (SSH agent, credential helper, etc.) — there is no in-server auth. Kontexta does not rate-limit, but the remote may. Idempotent in steady state: a no-op commit is skipped, but the push still runs. Throws if the project has no configured backup repo or if push fails (network, auth, conflict). Returns {success, copied_files_count, copied_paths}. Use after a batch of KB writes to get changes off-machine. |
| resources.clip_urlA | SIDE-EFFECTFUL — fetches an EXTERNAL URL and writes a NEW KB file. Downloads the page, extracts the main article via Readability, converts to markdown, and saves it under knowledge/urlclips/. Auto-classified as content_class='dictionary' (clipped external references are treated as authoritative reference material). NOT idempotent / no de-dup — re-clipping the same URL creates a second file. AUTH: anonymous by default; pass headers (e.g. {Cookie: 'session=...'} or {Authorization: 'Bearer ...'}) to clip behind logins. Kontexta does not rate-limit but the upstream may throttle. On auth-required pages returns isError with code: AUTH_REQUIRED, optional login_url, and a hint to retry with headers. Returns {file_id, path, title, source}. Use to ingest external docs into the KB. |
| files.get_historyA | Return the git commit history for one file (newest first), each entry with hash, message, date, and author. Reads the file's owning repo: the project's git repo for project files, the KB backup repo for KB files. Read-only; no side effects, auth, or rate limits. Returns {file_id, path, history}; an empty array means the file has not been committed yet. Use to understand a file's evolution before editing or restoring. Pair with files.get_diff to see exact line changes; use files.restore to roll back. |
| files.get_diffA | Return the unified diff of one file between two commit hashes (typically obtained from files.get_history for the same file). Read-only; no side effects, auth, or rate limits. Order matters — commit_a is treated as the earlier side; reversing the args inverts the diff. Throws if either hash is unknown to the file's repo. Use after files.get_history to see WHAT changed, not just THAT it changed. |
| files.restoreA | DESTRUCTIVE. Overwrite a file's current on-disk content with the version recorded at a specific git commit, then re-index FTS. The hash MUST come from files.get_history for THIS file (foreign hashes throw). The current uncommitted content is lost unless it was already committed elsewhere. The file watcher may also pick up the change before this returns. No external auth or rate limits. Returns {file_id, path, hash, success, message}. Use only to undo accidental edits or recover a known-good version. |
| files.read_outlineA | Return a flat list of markdown headings for one file (level, text, line, byteStart, byteEnd). Read-only; no side effects, auth, or rate limits. Use as a cheap probe before files.read({ id, section }) or files.update({ file_id, section, content }) so you don't spend tokens on the full body just to learn what sections exist. Empty outline means the file has no markdown headings (it may still have content — fall back to files.read in full or files.read({ id, lines })). |
| folders.listA | List folder paths under a project root (or the Knowledge Base when project_id is null/omitted). Returns {folders: string[], base_path} where folders are RELATIVE to base_path. Read-only; no side effects, auth, or rate limits. Throws if project_id references an unknown project. Use to discover where to drop a new file via files.create's folder argument or to navigate vault structure; to actually create one use folders.create. |
| folders.createA | Create a folder under a project root or the KB. Idempotent — creating an existing folder succeeds. Nested paths like notes/inbox create intermediates. REJECTS: empty names, null bytes, leading path separators, and any segment equal to .. (the call returns isError, no folder is touched). Side effect: a directory is mkdir'd on disk; no DB rows are written until a file lands inside. No external auth or rate limits. Returns {path, base_path}. |
| folders.deleteA | DESTRUCTIVE — recursively delete a folder under the KB AND every file inside it (disk + FTS rows). REFUSES (returns isError) when project_id is supplied: deleting inside a registered project would race the file watcher and re-ingest the contents — remove project content via your editor instead. Same name validation as folders.create. Not recoverable from Kontexta after the call (only the git backup, if configured, retains it). No external auth or rate limits. Returns {success: true}. |
| files.moveA | Move/rename a file. Destination 'new_path' must be absolute and resolve INSIDE the file's owning project or global knowledge directory. Cross-project moves are rejected. Alternative: pass kind='dictionary'|'note' (with no new_path) to move a KB file into the mirrored path in the other class tree — subfolder path is preserved. Operates locally with no auth or limits. |
| files.find_relatedA | Find other files sharing tags with the given file, ranked by shared_tag_count descending. Read-only; no side effects, auth, or rate limits. Returns annotated file rows with shared_tag_count and shared_tags. Empty result means the file has no tags or no other file shares them — try files.search/files.regex_search for content-based discovery, or tags.suggest to bootstrap labels first. kind narrows to one content class. Default limit 10. |
| tags.searchA | Bulk-tag — run an FTS search and append add_tags to every matching file in one call. Side effect: each match gets addTags applied (additive, idempotent per tag); the matched files themselves are NOT modified beyond their tag links. Per-file failures isolated to errors[]. No external auth or rate limits. There is NO dry-run flag, so ALWAYS run files.search with the same query first to verify the match set before tagging. The tags[] filter requires existing tags to ALL match (it scopes the search; it does not control which tags get added). Returns {matched_count, tagged_count, tags_applied, tagged_ids, errors}. |
| admin.overviewA | Vault-state snapshot. mode: 'stats' = aggregate counts for a scope: file_count, untagged_count, favorite_count, top_tags. With project_id omitted (everything), also returns by_project breakdown. include_token_total: true stat()s every matching file on disk to compute a body-size estimate — measurably slower on large vaults; default false. mode: 'whats_new' = list files created or modified since a checkpoint (since, REQUIRED for this mode — ISO-8601 like 2025-01-15T00:00:00Z or relative durations like 1h/7d/2w; invalid formats throw); CAVEAT: hard-deleted files are NOT surfaced, only mtime-driven changes. Both modes: project_id: null = KB only; omit = everything. Read-only; no side effects, auth, or rate limits. Use stats as a cheap dashboard or to spot untagged content for cleanup (for live disk-vs-index drift use files.diff_against_disk); use whats_new at session start to catch up. |
| tags.suggestA | Propose tags for a file by mining the existing tag corpus via FTS — picks distinctive terms from the file (≥4 chars, stopword-filtered) and returns tags applied to other files that score high on those terms. No LLM, no network. Already-applied tags are excluded so the suggestions are net-new. Read-only; no side effects, auth, or rate limits. Returns {file_id, path, existing_tags, suggestions: [{tag, score, sources}]}. Empty suggestions = no distinctive terms or no overlap with the existing taxonomy yet — bootstrap with tags.add first. Default limit 10, max 50. Suggestions are NOT auto-applied. |
| files.diff_against_diskA | Diagnose drift between one file's disk content and its FTS index. Status is one of in_sync, diverged, disk_unreadable, or no_index_row. On divergence returns sizes, line counts, the first divergent line number, and the disk vs index sample for that line — NOT a full diff (use files.get_diff for full diffs between commits). Read-only; no side effects, auth, or rate limits. Use when search results look stale; if status is diverged or no_index_row, run projects.refresh_index to fix. |
| projects.refresh_indexA | Reconcile the FTS index against disk. For a project (project_id set), re-runs discoverFiles. For the KB (project_id null/omitted), walks knowledge/, ingests new .md files, reindexes any whose content hash drifted, and PRUNES rows for files no longer on disk. SIDE-EFFECTFUL: writes/updates/deletes file and FTS rows (the prune is destructive on stale index rows but never deletes files from disk). Idempotent — running twice is a near no-op. Skips files >5MB and standard junk dirs (node_modules, .git, dist, build, etc.). No external auth or rate limits. Returns {scope, newly_indexed, refreshed, pruned}. Use after editing files outside Kontexta, or when files.diff_against_disk reports drift. |
| projects.mapA | Return a compact indented outline of folders, file titles, tags, and IDs in a single dense block — substantially fewer tokens than the equivalent files.list JSON for the same scope. Read-only; no side effects, auth, or rate limits. Capped at max_lines (default 5000); the response reports est_tokens and emits a warning field if it exceeds KONTEXTA_PROJECT_TOKEN_WARN. project_id: null = KB only; omit = everything. Defaults: include_tags=true, show_titles=true. Use to orient yourself in an unfamiliar vault or project; for keyword lookup use files.search. |
| hands.listA | List every Hands command tool currently registered, with project scope, tool name, danger level, confirmation flag, and description. Hands tools come from per-project kontexta.json files loaded at register time. Pass schema: true to instead get the complete kontexta.json authoring reference (JSON schema, validation rules, security guarantees, limitations, annotated example) — a static document, unrelated to any specific registered hand. Read-only; no side effects, auth, or rate limits. Use the default list mode to discover what side-effectful project commands the agent is permitted to run; use schema: true when helping a user write or fix a kontexta.json; reload after editing one with hands.reload. |
| hands.reloadA | Re-scan every registered project's kontexta.json and rebuild the live Hands tool registry — newly-declared tools become callable immediately, removed tools disappear from tools/list. SIDE EFFECT is on the running MCP session's tool inventory only (no disk writes). Idempotent. No external auth or rate limits. Takes no parameters. Returns per-project load results (counts of registered/disabled tools and any validation warnings). Use after editing a kontexta.json mid-session; for the schema see hands.list({ schema: true }). |
| hands.confirmA | Approve and EXECUTE a previously-issued Hands invocation by its single-use approval token. The token is returned by any confirm-required Hands tool; tokens expire after 60 seconds and CANNOT be reused. Side effect equals whatever the underlying Hand does — this can be highly destructive (running arbitrary shell commands, modifying files, etc.), so only call when the user has authorised the pending action. The token IS the auth (no external auth, no rate limits). Invalid, expired, or already-consumed tokens return an inert text response, NOT an error. |
| admin.get_profileA | Return the user profile stored in the Knowledge Base. The profile helps AI agents understand the user's context, role, preferences, and goals. Read-only; no side effects, auth, or rate limits. Returns existence status, full content, list of missing required sections, and a hint for new users. Use at session start to understand who you're working with. |
| journal.distillA | Run the distillation pipeline: read raw events since the high-water mark, group by topic, write mechanical markdown entries, advance high-water. Idempotent. Auto-provisions a project row for orphan slugs (e.g. default) that have no registered project yet. |
| journal.statusB | Report the journal backlog and high-water mark for a project. |
| journal.commit_upgradesA | After dispatching subagents to upgrade mechanical journal entries to LLM-narrative, call this with the affected task slugs. Updates journal_meta.status_latest to mark the entries as upgraded. |
| journal.housekeepA | Run journal retention/archival for a project. Idempotent. Prunes old raw .jsonl files and archives cold tasks per the configured retention policy. |
| calendar.entities.addA | SIDE-EFFECTFUL. Create a new tracked entity — any named thing you schedule events against (a server, a delivery van, a store location, a piece of equipment, a room, etc.). Not idempotent: a duplicate name (case-insensitive) throws. Returns {entity}. Use calendar.entities.link afterwards to record dependencies for conflict detection. |
| calendar.entities.updateA | SIDE-EFFECTFUL. Patch an existing entity's fields, including active (set false to soft-retire it without losing its history). Idempotent per patch. Returns {entity}. For a hard delete see calendar.entities.delete. |
| calendar.entities.deleteA | DESTRUCTIVE. Permanently delete an entity AND cascade-delete every event and link attached to it. Not idempotent — deleting an unknown id throws. Returns {success, deleted_events, deleted_links}. To deactivate without losing history, use calendar.entities.update with active: false instead. |
| calendar.entities.listA | Read-only; no side effects, auth, or rate limits. List tracked entities, each annotated with its outgoing and incoming dependency links. Returns {entities, count}. |
| calendar.entities.linkA | SIDE-EFFECTFUL. Create or update a directed dependency edge between two entities (e.g. "A feeds B"), or remove one with remove: true. Idempotent — upserts the label on repeat calls; removing an absent link is a no-op. Used by calendar.events.conflicts/calendar.events.list to flag overlaps across connected entities (one hop, either direction). Returns {link} or {removed}. |
| calendar.events.addA | SIDE-EFFECTFUL. Add a one-off time window (downtime, maintenance, a delivery, a shift, an inspection, etc.) to an entity. NOT idempotent — calling this twice creates two events. starts_at/ends_at must be ISO 8601 with an explicit timezone (Z or ±HH:MM) — naive timestamps are rejected because their meaning would be ambiguous once stored as UTC. Returns {event}. Follow up with calendar.events.conflicts to check for overlaps. |
| calendar.events.updateA | SIDE-EFFECTFUL. Patch an existing event (move it, retitle it, re-home it to a different entity, etc.). The merged result is re-validated — shrinking ends_at below starts_at throws. Returns {event}. |
| calendar.events.deleteA | DESTRUCTIVE. Delete one event by id. Idempotent — deleting an already-absent id is a no-op. Returns {success, existed}. |
| calendar.events.listA | Read-only; no side effects, auth, or rate limits. List events overlapping a window (half-open — an event ending exactly at from is excluded), optionally filtered by entity/type. Set include_conflicts to also run conflict detection over the same window and attach it. Returns {events, count, conflicts?}. For conflicts alone, prefer calendar.events.conflicts. |
| calendar.events.conflictsA | Read-only; no side effects, auth, or rate limits. Report scheduling conflicts in a window: overlaps on the same entity (overlap), overlaps between linked entities one hop apart (linked_overlap), and gaps smaller than a minimum buffer (insufficient_buffer). Computed on demand — nothing is persisted. Buffer defaults to the calendar.min_buffer_minutes setting (0 = off); pass buffer_minutes to override for this call. Returns {conflicts, count, buffer_minutes, events_considered}. See also calendar.events.list with include_conflicts. |
| calendar.export_icsA | Read-only; no side effects, auth, or rate limits. Export events in a window as an RFC 5545 ICS calendar (UTC times, no VTIMEZONE needed) for import into Outlook/Calendar apps. Returns {ics, event_count} with the calendar text as a JSON string field. |