# Microsoft Calendar CLI

Outlook calendars, events, free/busy windows, and open slots through Microsoft Graph.

## Install

```bash
cmdhub get mscal
```

## Authentication

OAuth2

## Agent-Readable Catalog

- Machine-readable command catalog: `/cli/mscal/catalog.json`
- Stable command anchors use the `cmd-...` IDs listed under each command.
- Inspect this catalog before planning tool use for command paths, examples, flags, mutation markers, and dry-run support.

## Overview

```bash
mscal auth login

# Review today's calendar and inspect one event
mscal event list --today --limit 10
mscal event get e1

# Search, inspect, and resolve through short refs
mscal event search --query "project review" --limit 1 --jsonl \
  | mscal event get

mscal resolve e1

# Create and clean up an event
mscal event create --title "Project review" --start "tomorrow 14:00" --duration 1h
mscal event delete e1
```

## CLI Overview

Microsoft Outlook Calendar adapter CLI

Work with Microsoft Outlook Calendar calendars, events, free/busy windows, and open slots.

The intended workflow is:
- list calendars or events first
- follow up with a short ref such as c1 or e1, or pipe jsonl into the next command
- use resolve when you need to inspect what a short ref expands to

Search queries are plain Microsoft full-text strings. Field prefixes such as title:
are not supported by the current CLI surface.

Use --jsonl for self-pipe flows such as:
  mscal event list --today --jsonl | mscal event get
  mscal event search --query dentist --jsonl | mscal event get
  mscal event search --query dentist --limit 1 --jsonl | mscal event update --location "Room 4"
  mscal event search --query dentist --limit 1 --jsonl | mscal event delete --all --yes

## Generated Coverage

- Command groups: `14`
- Total documented command nodes: `41`
- Live examples: `6`
- Lifecycle surfaces: `auth`, `doctor`, `capabilities`, `schema`, `resolve`, `version`
- Machine-readable output: `--json`, `--jsonl`
- Human views: `summary`, `full`

## Auth Contract

- Auth mode: OAuth2
- Provider permission model: Microsoft OAuth
- Auth scopes: `offline_access`, `User.Read`, `Calendars.ReadWrite`
- Scope note: Used for Outlook calendars, events, and availability.
- Standard commands: `auth login`, `auth logout`, `auth refresh`, `auth status`, `auth whoami`
- `auth status --json` reports `tool`, `profile`, `provider`, `authenticated`, and `suggested_next_step`.
- `auth whoami --json` is the stable way to answer identity questions.

## Agent Safety Notes

- Discovery commands such as `list`, `search`, `get`, `resolve`, `schema`, `doctor`, `capabilities`, `status`, and `whoami` are the safest commands to run first.
- Treat commands marked as mutating as state-changing local or provider operations.
- Mutating command paths: `mscal auth login`, `mscal auth logout`, `mscal auth refresh`, `mscal event create`, `mscal event delete`, `mscal event quick-add`, `mscal event update`, `mscal profile create`, `mscal profile delete`, `mscal profile use`
- No generated `--dry-run` flag was detected for this CLI. Use discovery commands before mutating commands.

## Global Flags

- `--json` (bool): Emit JSON machine output
- `--jsonl` (bool): Emit JSONL records for piping
- `--output` (string): Compatibility output mode: json|jsonl. Prefer --json or --jsonl
- `--view` (string): View mode: summary|full Default: `summary`.
- `--profile` (string): Config/auth profile Default: `default`.
- `--config` (string): Config path override
- `--quiet` (bool): Suppress non-essential diagnostics
- `--debug` (bool): Enable debug diagnostics
- `--limit` (int): Maximum items to emit Default: `50`.
- `--page-size` (int): Requested upstream page size Default: `50`.
- `--cursor` (string): Continuation cursor
- `--no-paginate` (bool): Fetch only one page
- `--version` (bool): Print version information

## Command Reference

### `mscal auth`

- Anchor: `cmd-mscal-auth`
- Mutates state: `no`
- Supports dry run: `no`

Manage Microsoft Outlook Calendar authentication

#### `mscal auth login`

- Anchor: `cmd-mscal-auth-login`
- Mutates state: `yes`
- Supports dry run: `no`

Authenticate with Microsoft Outlook Calendar

Example:

```bash
mscal auth login
mscal auth login --help
mscal auth status
```

#### `mscal auth logout`

- Anchor: `cmd-mscal-auth-logout`
- Mutates state: `yes`
- Supports dry run: `no`

Remove stored credentials

Example:

```bash
mscal auth login
mscal auth logout
mscal auth logout --help
```

#### `mscal auth refresh`

- Anchor: `cmd-mscal-auth-refresh`
- Mutates state: `yes`
- Supports dry run: `no`

Refresh stored token

Example:

```bash
mscal auth refresh
mscal auth refresh --help
mscal auth status
```

#### `mscal auth status`

- Anchor: `cmd-mscal-auth-status`
- Mutates state: `no`
- Supports dry run: `no`

Show auth status

Example:

```bash
mscal auth status
mscal auth status --help
mscal auth status --view full
```

#### `mscal auth whoami`

- Anchor: `cmd-mscal-auth-whoami`
- Mutates state: `no`
- Supports dry run: `no`

Show current calendar identity

Example:

```bash
mscal auth whoami
mscal auth whoami --help
mscal auth whoami --json
```

### `mscal calendar`

- Anchor: `cmd-mscal-calendar`
- Mutates state: `no`
- Supports dry run: `no`

Manage calendars

#### `mscal calendar get`

- Anchor: `cmd-mscal-calendar-get`
- Mutates state: `no`
- Supports dry run: `no`

Get a calendar

Example:

```bash
mscal calendar get
mscal calendar get c1
mscal calendar get c1 c2
mscal calendar get primary --view full
mscal calendar list --jsonl | mscal calendar get
```

#### `mscal calendar list`

- Anchor: `cmd-mscal-calendar-list`
- Mutates state: `no`
- Supports dry run: `no`

List calendars

Example:

```bash
mscal calendar list
mscal calendar list --jsonl | mscal calendar get
mscal calendar list --view full
```

#### `mscal calendar search`

- Anchor: `cmd-mscal-calendar-search`
- Mutates state: `no`
- Supports dry run: `no`

Search calendars by summary

Example:

```bash
mscal calendar search
mscal calendar search --query holidays
mscal calendar search --query work --view full
```

Flags:

- `--query` (string): Search text

### `mscal capabilities`

- Anchor: `cmd-mscal-capabilities`
- Mutates state: `no`
- Supports dry run: `no`

Describe mscal capabilities, scopes, output modes, and schemas

Describe mscal capabilities, scopes, schemas, and the command contract.

The default machine output includes the full command contract so help, schemas,
guidance, and conformance use the same inventory. Use --section for focused
human or JSON views of scopes, schemas, or command paths.

Example:

```bash
mscal capabilities
mscal capabilities --help
mscal capabilities --json
mscal capabilities --section scopes
```

Flags:

- `--section` (string): Focused section: scopes, schemas, or commands

### `mscal config`

- Anchor: `cmd-mscal-config`
- Mutates state: `no`
- Supports dry run: `no`

Inspect local configuration

#### `mscal config explain`

- Anchor: `cmd-mscal-config-explain`
- Mutates state: `no`
- Supports dry run: `no`

Explain resolved configuration

Example:

```bash
mscal config explain --help
```

### `mscal doctor`

- Anchor: `cmd-mscal-doctor`
- Mutates state: `no`
- Supports dry run: `no`

Run mscal diagnostics

Example:

```bash
mscal doctor
mscal doctor --help
mscal doctor --view full
```

### `mscal event`

- Anchor: `cmd-mscal-event`
- Mutates state: `no`
- Supports dry run: `no`

Manage calendar events

#### `mscal event create`

- Anchor: `cmd-mscal-event-create`
- Mutates state: `yes`
- Supports dry run: `no`

Create an event

Create a timed or all-day event.

Time fields accept natural values such as "tomorrow 14:00", relative values
such as +1h, or RFC3339 timestamps such as 2026-05-30T14:00:00+02:00.
Use --duration with --start for the common RFC3339 + duration + location shape.
Timed events without --end or --duration default to 30 minutes.

Adding --attendee, --optional-attendee, or --room creates a Microsoft meeting
and sends provider invitations. Pass --send-invites to confirm that external
effect.

Example:

```bash
mscal event create --calendar c1 --title "Planning" --start 2026-05-30T14:00:00+02:00 --duration 45m --location "Room 4"
mscal event create --help
mscal event create --title "Company holiday" --start 2026-05-01 --all-day
mscal event create --title "Interview" --start +2h --duration 45m --attendee alex@example.com --room conf-a@example.com --send-invites
mscal event create --title "Project review" --start "tomorrow 14:00" --duration 45m
```

Flags:

- `--all-day` (bool): All-day event
- `--attendee` (stringArray): Required attendee email; repeat for multiple attendees
- `--calendar` (string): Calendar selector Default: `primary`.
- `--description` (string): Description
- `--duration` (string): Duration
- `--end` (string): Event end
- `--location` (string): Location
- `--optional-attendee` (stringArray): Optional attendee email; repeat for multiple attendees
- `--room` (stringArray): Room email or resource attendee; repeat for multiple rooms
- `--send-invites` (bool): Confirm that attendee or room flags send Microsoft meeting invitations
- `--start` (string): Event start
- `--title` (string): Event title
- `--yes` (bool): Accepted for compatibility; this command is already non-interactive

#### `mscal event delete`

- Anchor: `cmd-mscal-event-delete`
- Mutates state: `yes`
- Supports dry run: `no`

Delete an event

Example:

```bash
mscal event delete --all --yes e1 e2
mscal event delete --help
mscal event delete e1
mscal event search --query cmdhub-human-audit --jsonl | mscal event delete --all --yes
```

Flags:

- `--all` (bool): Confirm batch event delete
- `--calendar` (string): Optional calendar selector
- `--yes` (bool): Confirm destructive batch event delete

#### `mscal event get`

- Anchor: `cmd-mscal-event-get`
- Mutates state: `no`
- Supports dry run: `no`

Get an event

Get event details by local ref, provider ID, or event JSONL from stdin.

When stdin contains event JSONL items from event list/search, event get consumes
the item id/provider_id plus calendar_id fields, fetches current provider
details, and renders the retrieved event. The get output is already the usable
detail view; a second "mscal event get e1" call is only needed if you
intentionally want to re-open a new local ref.

Example:

```bash
mscal event get
mscal event get e1
mscal event get e1 --view full
mscal event get e1 e2
mscal event list --today --jsonl | mscal event get
mscal event search --query audit --limit 1 --jsonl | mscal event delete --all --yes
mscal event search --query audit --limit 1 --jsonl | mscal event get
mscal event search --query audit --limit 1 --jsonl | mscal event update --location "Room 4"
```

Flags:

- `--calendar` (string): Optional calendar selector

#### `mscal event list`

- Anchor: `cmd-mscal-event-list`
- Mutates state: `no`
- Supports dry run: `no`

List events

Example:

```bash
mscal event list
mscal event list --calendar c1 --from now --to +72h
mscal event list --today
mscal event list --week --view full
```

Flags:

- `--calendar` (string): Calendar selector Default: `primary`.
- `--from` (string): Start time
- `--to` (string): End time; relative values such as +4h are relative to --from
- `--today` (bool): Today window
- `--tomorrow` (bool): Tomorrow window
- `--week` (bool): Week window

#### `mscal event quick-add`

- Anchor: `cmd-mscal-event-quick-add`
- Mutates state: `yes`
- Supports dry run: `no`

Create an event from natural text

Example:

```bash
mscal event quick-add --calendar c1 --text "Planning review today 3pm"
mscal event quick-add --help
mscal event quick-add --text "Lunch with Sam tomorrow 12pm"
```

Flags:

- `--calendar` (string): Calendar selector Default: `primary`.
- `--text` (string): Quick-add text
- `--yes` (bool): Accepted for compatibility; this command is already non-interactive

#### `mscal event search`

- Anchor: `cmd-mscal-event-search`
- Mutates state: `no`
- Supports dry run: `no`

Search events

Search events using Microsoft Calendar full-text query semantics.

Use plain text or tokens in --query, such as "dentist" or "project review".
Field-prefix filters such as title:review are not supported by this CLI surface.

For single-event detail, pipe JSONL search results into event get:
  mscal event search --query "project review" --jsonl | mscal event get

The same JSONL search stream can feed update or delete when it contains the exact
event you intend to mutate:
  mscal event search --query "project review" --limit 1 --jsonl | mscal event update --location "Room 4"
  mscal event search --query "project review" --limit 1 --jsonl | mscal event delete --all --yes

Example:

```bash
mscal event search
mscal event search --query "project review" --from today --to tomorrow
mscal event search --query audit --jsonl | mscal event get
mscal event search --query audit --limit 1 --jsonl | mscal event delete --all --yes
mscal event search --query audit --limit 1 --jsonl | mscal event update --location "Room 4"
mscal event search --query dentist
```

Flags:

- `--calendar` (string): Calendar selector Default: `primary`.
- `--from` (string): Start time
- `--query` (string): Search query
- `--to` (string): End time; relative values such as +4h are relative to --from

#### `mscal event update`

- Anchor: `cmd-mscal-event-update`
- Mutates state: `yes`
- Supports dry run: `no`

Update an event

Update event fields.

Adding or changing attendees creates or updates a Microsoft meeting and can send
provider invitations or updates. Pass --send-updates all when using attendee or
room flags so the notification behavior is explicit.

Example:

```bash
mscal event search --query review --jsonl | mscal event update --location "Room 4"
mscal event update --help
mscal event update e1 --attendee alex@example.com --send-updates all
mscal event update e1 --start "tomorrow 15:00" --duration 30m
mscal event update e1 --title "Updated title"
```

Flags:

- `--all-day` (bool): All-day event
- `--attendee` (stringArray): Required attendee email; repeat for multiple attendees
- `--calendar` (string): Optional calendar selector
- `--description` (string): Description
- `--duration` (string): Duration
- `--end` (string): Event end
- `--location` (string): Location
- `--optional-attendee` (stringArray): Optional attendee email; repeat for multiple attendees
- `--room` (stringArray): Room email or resource attendee; repeat for multiple rooms
- `--send-updates` (string): Confirm attendee or room update notifications: all
- `--start` (string): Event start
- `--title` (string): Event title
- `--yes` (bool): Accepted for compatibility; this command is already non-interactive

### `mscal freebusy`

- Anchor: `cmd-mscal-freebusy`
- Mutates state: `no`
- Supports dry run: `no`

Inspect busy windows

#### `mscal freebusy get`

- Anchor: `cmd-mscal-freebusy-get`
- Mutates state: `no`
- Supports dry run: `no`

Get busy windows for calendars

Example:

```bash
mscal freebusy get
mscal freebusy get --calendar c1 --from today --to tomorrow
mscal freebusy get --from "tomorrow 09:00" --to "tomorrow 17:00" --view full
mscal freebusy get --from now --to +4h
mscal freebusy get --user alex@example.com --from "tomorrow 09:00" --to "tomorrow 17:00"
```

Flags:

- `--calendar` (stringSlice): Calendar selectors Default: `[primary]`.
- `--from` (string): Start time
- `--schedule` (stringSlice): Alias for --user; Microsoft Graph schedule email to include
- `--to` (string): End time; relative values such as +4h are relative to --from
- `--user` (stringSlice): User or schedule email to include; repeat or comma-separate

### `mscal meeting-time`

- Anchor: `cmd-mscal-meeting-time`
- Mutates state: `no`
- Supports dry run: `no`

Find Microsoft Graph meeting-time suggestions

#### `mscal meeting-time find`

- Anchor: `cmd-mscal-meeting-time-find`
- Mutates state: `no`
- Supports dry run: `no`

Find meeting-time suggestions

Find Microsoft Graph meeting-time suggestions for attendees and rooms.

This uses Microsoft Graph findMeetingTimes. Some personal Microsoft accounts or
tenants do not support this scheduling surface; in that case mscal returns a
provider-limit recovery screen and suggests free/busy or event list fallbacks.

Example:

```bash
mscal meeting-time find
mscal meeting-time find --attendee alex@example.com --from "tomorrow 09:00" --to "tomorrow 17:00" --duration 30m
mscal meeting-time find --attendee alex@example.com --room conf-a@example.com --from now --to +8h
```

Flags:

- `--attendee` (stringSlice): Required attendee email; repeat or comma-separate
- `--duration` (string): Meeting duration Default: `30m`.
- `--from` (string): Start time
- `--room` (stringSlice): Room email; repeat or comma-separate
- `--to` (string): End time; relative values such as +4h are relative to --from

### `mscal profile`

- Anchor: `cmd-mscal-profile`
- Mutates state: `no`
- Supports dry run: `no`

Manage provider profiles

#### `mscal profile create`

- Anchor: `cmd-mscal-profile-create`
- Mutates state: `yes`
- Supports dry run: `no`

Create an unauthenticated provider profile

Example:

```bash
mscal profile create --help
```

#### `mscal profile delete`

- Anchor: `cmd-mscal-profile-delete`
- Mutates state: `yes`
- Supports dry run: `no`

Delete a local provider profile

Example:

```bash
mscal profile delete --help
```

#### `mscal profile list`

- Anchor: `cmd-mscal-profile-list`
- Mutates state: `no`
- Supports dry run: `no`

List provider profiles

Example:

```bash
mscal profile list --help
```

#### `mscal profile rename`

- Anchor: `cmd-mscal-profile-rename`
- Mutates state: `no`
- Supports dry run: `no`

Rename a local provider profile

Example:

```bash
mscal profile rename --help
```

#### `mscal profile use`

- Anchor: `cmd-mscal-profile-use`
- Mutates state: `yes`
- Supports dry run: `no`

Set the default provider profile

Example:

```bash
mscal profile use --help
```

### `mscal resolve`

- Anchor: `cmd-mscal-resolve`
- Mutates state: `no`
- Supports dry run: `no`

Resolve a marker or short id

Example:

```bash
mscal resolve --help
mscal resolve c1
mscal resolve e1
mscal resolve u1
```

### `mscal room`

- Anchor: `cmd-mscal-room`
- Mutates state: `no`
- Supports dry run: `no`

Discover Microsoft 365 rooms

#### `mscal room get`

- Anchor: `cmd-mscal-room-get`
- Mutates state: `no`
- Supports dry run: `no`

Get a room

Example:

```bash
mscal room get
mscal room get r1
mscal room search --query conference --jsonl | mscal room get
```

#### `mscal room list`

- Anchor: `cmd-mscal-room-list`
- Mutates state: `no`
- Supports dry run: `no`

List rooms

Example:

```bash
mscal room list
mscal room list --limit 20 --jsonl
```

#### `mscal room search`

- Anchor: `cmd-mscal-room-search`
- Mutates state: `no`
- Supports dry run: `no`

Search rooms

Example:

```bash
mscal room search
mscal room search --query "floor 4" --jsonl
mscal room search --query conference
```

Flags:

- `--query` (string): Room search text

### `mscal schema`

- Anchor: `cmd-mscal-schema`
- Mutates state: `no`
- Supports dry run: `no`

Emit mscal schema hints

Example:

```bash
mscal schema
mscal schema --help
mscal schema event.create --input
mscal schema event.create --out
```

Flags:

- `--in` (bool): Show input schema
- `--input` (bool): Show input schema
- `--out` (bool): Show output schema

### `mscal slot`

- Anchor: `cmd-mscal-slot`
- Mutates state: `no`
- Supports dry run: `no`

Find open slots

#### `mscal slot find`

- Anchor: `cmd-mscal-slot-find`
- Mutates state: `no`
- Supports dry run: `no`

Find open slots across calendars

Example:

```bash
mscal slot find
mscal slot find --calendar c1 --from today --to tomorrow --duration 1h
mscal slot find --from "tomorrow 09:00" --to "tomorrow 17:00" --view full
mscal slot find --from now --to +4h --duration 30m
```

Flags:

- `--calendar` (stringSlice): Calendar selectors Default: `[primary]`.
- `--duration` (string): Minimum slot duration Default: `30m`.
- `--from` (string): Start time
- `--to` (string): End time; relative values such as +4h are relative to --from

### `mscal version`

- Anchor: `cmd-mscal-version`
- Mutates state: `no`
- Supports dry run: `no`

Show mscal version information

Example:

```bash
mscal version
mscal version --help
mscal version --json
```

## Live Examples

### Auth

#### Check current Microsoft calendar identity

Returns the active Microsoft calendar identity.

_Example metadata: requires auth; provider state: live._

```bash
mscal auth whoami
```

```text
Authenticated as kestrelphilip@outlook.com

Next steps:
- Calendars:    mscal calendar list
- Events:       mscal event list --week
- Capabilities: mscal capabilities
```

### Calendars

#### List calendars

Lists visible calendars.

_Example metadata: requires auth; provider state: live._

```bash
mscal calendar list --limit 5
```

```text
REF  TITLE                  STATUS
c1   Calendar               primary, writer
c2   Birthdays              selected, reader
c3   South Africa holidays  selected, reader

Next steps:
- Open:         mscal calendar get c1
- Events:       mscal event list --calendar c1 --week
- Freebusy:     mscal freebusy get --calendar c1 --from {time} --to +8h
- Capabilities: mscal capabilities
```

### Events

#### List today's events

Lists events happening today.

_Example metadata: requires auth; provider state: live._

```bash
mscal event list --today --limit 5
```

```text
No events found.

Next steps:
- List week:   mscal event list --calendar primary --from 2026-05-30T00:00:00+02:00 --to 2026-05-31T00:00:00+02:00
- Create hold: mscal event create --calendar primary --title Hold --start +1h --duration 30m
- Status:      mscal auth status
- Calendars:   mscal calendar list
```

#### Search events

Searches events by text.

_Example metadata: requires auth; provider state: live._

```bash
mscal event search --query eval --limit 5
```

```text
No events matched "eval".

Next steps:
- List week:   mscal event list --calendar primary --from 2026-05-30T18:02:12+02:00 --to 2026-06-02T18:02:12+02:00
- Create hold: mscal event create --calendar primary --title Hold --start +1h --duration 30m
- Status:      mscal auth status
- Calendars:   mscal calendar list
```

### Freebusy

#### Inspect busy windows

Shows busy windows on the primary calendar.

_Example metadata: requires auth; provider state: live._

```bash
mscal freebusy get --calendar primary --from now --to +8h
```

```text
Status: free
Busy windows
Field      Value
---------  -----
Window     Sat 30 May 18:02-Sun 31 May 02:02 Local
Calendars  primary

No busy intervals found.

Next steps:
- Find slots: mscal slot find --calendar primary --from 2026-05-30T16:02:13Z --to 2026-05-31T00:02:13Z --duration 30m
- Events:     mscal event list --calendar primary --from 2026-05-30T16:02:13Z --to 2026-05-31T00:02:13Z
- Calendars:  mscal calendar list
```

### Diagnostics

#### Run diagnostics

Checks auth, scopes, and Microsoft Graph calendar connectivity.

_Example metadata: requires auth; provider state: live._

```bash
mscal doctor
```

```text
mscal doctor: WARN
Account: kestrelphilip@outlook.com
Checks: {id} ok, {id} warn, profiles ok, microsoft_oauth ok, token ok, scopes ok, mscal_api ok, local_timezone ok
CHECK                STATUS  DETAIL
{id}   OK      using file
{id}  WARN    credential backend stores local plaintext secrets
profiles             OK      active profile "kestrelphilip@outlook.com"
microsoft_oauth      OK      built-in Microsoft provider client is configured
token                OK      token expires at 2026-05-30T16:33:17Z
scopes               OK      stored token covers required Microsoft Outlook Calendar scopes
mscal_api            OK      calendar API reachable
local_timezone       OK      Local

Next steps:
- Status:       mscal auth status
- Capabilities: mscal capabilities
```
