245 lines
8.9 KiB
Markdown
245 lines
8.9 KiB
Markdown
# O365 Calendar Integration — Design Spec
|
|
|
|
## Goal
|
|
|
|
Integrate Microsoft Office 365 calendar with the Obsidian vault to enable meeting note creation with automatic participant matching, series recognition, and agenda pre-population. Two creation paths: manual (Templater) and Claude-assisted (MCP).
|
|
|
|
## Scope
|
|
|
|
- **In scope:** Calendar read access, meeting note creation, participant matching, series recognition, agenda import
|
|
- **Out of scope:** Email integration (Phase 2), write-back to O365, automatic background sync, Copilot/Work IQ integration
|
|
|
|
## Architecture Overview
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────┐
|
|
│ Obsidian Vault │
|
|
│ │
|
|
│ 03 Bereiche/Meetings/ 00 Kontext/Personen/ │
|
|
│ ┌──────────────────┐ ┌──────────────────┐ │
|
|
│ │ Meeting Notes │◄───│ Person Notes │ │
|
|
│ │ (per instance) │ │ (email matching) │ │
|
|
│ └──────────────────┘ └──────────────────┘ │
|
|
│ ▲ ▲ ▲ │
|
|
│ │ │ │ │
|
|
│ Templater Claude Person-Matcher │
|
|
│ Template (MCP) (shared logic) │
|
|
│ │ │ │
|
|
└──────────┼──────────┼─────────────────────────────┘
|
|
│ │
|
|
▼ ▼
|
|
┌──────────────────────┐
|
|
│ Microsoft Graph API │
|
|
│ (Azure App Reg) │
|
|
└──────────────────────┘
|
|
```
|
|
|
|
### Components
|
|
|
|
| Component | Purpose | Location |
|
|
|---|---|---|
|
|
| MCP Server (Softeria) | Claude reads O365 calendar | `~/.claude/` config |
|
|
| Node.js User Script | Templater reads O365 calendar | `scripts/o365-calendar.js` |
|
|
| Person Matcher | Email → person note, auto-create | `scripts/person-matcher.js` |
|
|
| Meeting Template | Unified format for both paths | Templater template folder |
|
|
| Frontmatter extensions | `email` on persons, `domain` on companies | Existing notes |
|
|
|
|
## Vault Structure Changes
|
|
|
|
### New folder
|
|
|
|
- `03 Bereiche/Meetings/` — all meeting notes, flat, filename `YYYY-MM-DD Title.md`
|
|
|
|
### Person frontmatter extension
|
|
|
|
```yaml
|
|
# New field added to all person notes
|
|
email: christopher.klein@krah.de # Primary match key
|
|
```
|
|
|
|
### Company frontmatter extension
|
|
|
|
```yaml
|
|
# New field added to company notes
|
|
domain: krah-gruppe.de # For auto-assignment person → company via email domain
|
|
```
|
|
|
|
### Auto-created person notes
|
|
|
|
When a meeting participant has no match in the vault:
|
|
|
|
```yaml
|
|
---
|
|
tags: [person]
|
|
vorname: Max
|
|
nachname: Müller
|
|
email: max.mueller@landata.de
|
|
kategorie: Extern
|
|
firma: "[[LANdata]]" # Wikilink if company note with matching domain exists
|
|
# Plain text "landata.de" otherwise
|
|
status: ungeprüft # Flag for manual review
|
|
---
|
|
|
|
# Max Müller
|
|
|
|
## Zur Person
|
|
|
|
- **E-Mail:** max.mueller@landata.de
|
|
- **Firma:** LANdata (landata.de)
|
|
|
|
## Notizen
|
|
|
|
```
|
|
|
|
### Auto-created company notes
|
|
|
|
Companies are NOT auto-created. Only the domain is stored as plain text in the person note until a company note is manually created.
|
|
|
|
## Meeting Note Template
|
|
|
|
```markdown
|
|
---
|
|
tags: [meeting]
|
|
date: YYYY-MM-DD
|
|
start: "HH:MM"
|
|
end: "HH:MM"
|
|
serie: "Serie Name" # Empty if not recurring
|
|
projekt: # Wikilink to project if assignable
|
|
teilnehmer:
|
|
- "[[Person 1]]"
|
|
- "[[Person 2]]"
|
|
status: offen # offen / abgeschlossen
|
|
o365_id: "AAMkAG..." # For back-reference to O365
|
|
---
|
|
|
|
# Meeting Title
|
|
|
|
## Teilnehmer
|
|
- [[Person 1]] — Role/Context
|
|
- [[Person 2]]
|
|
|
|
## Agenda
|
|
- Imported agenda items
|
|
|
|
## Notizen
|
|
|
|
|
|
## Entscheidungen
|
|
|
|
|
|
## Aufgaben
|
|
|
|
```
|
|
|
|
## Matching Logic
|
|
|
|
### Participant matching (priority order)
|
|
|
|
1. **Email match:** Search `00 Kontext/Personen/` for matching `email` frontmatter field
|
|
2. **Name fallback:** Compare O365 `displayName` against `vorname` + `nachname` in person notes
|
|
3. **Auto-create:** No match → create new person note with `status: ungeprüft`, derive company from email domain
|
|
|
|
### Company matching via email domain
|
|
|
|
1. Extract domain from email address (e.g., `landata.de` from `max@landata.de`)
|
|
2. Search `00 Kontext/Firmen/` for matching `domain` frontmatter field
|
|
3. Match found → Wikilink to company note
|
|
4. No match → store domain as plain text
|
|
|
|
### Series recognition
|
|
|
|
1. Check O365 event for `seriesMasterId` → recurring meeting
|
|
2. Match event title against folder names in `03 Bereiche/Jour Fixe/*/Agenda.md`
|
|
3. Match found → populate `serie` field, link to Jour Fixe agenda
|
|
|
|
### Agenda pre-population (priority order)
|
|
|
|
1. **Jour Fixe agenda:** Series recognized → import open items (`🔴 Offen` section) from `03 Bereiche/Jour Fixe/[Serie]/Agenda.md`
|
|
2. **O365 event body:** No series or Jour Fixe empty → use calendar event body text
|
|
3. **Empty:** Both empty → empty agenda section
|
|
|
|
## Creation Path A: Manual via Templater
|
|
|
|
1. User triggers template (hotkey or command palette)
|
|
2. Templater User Script calls Node.js helper
|
|
3. Node.js script authenticates via Azure App Registration (Delegated OAuth2 flow)
|
|
4. Fetches calendar events (today + next 7 days)
|
|
5. Presents selection list: `📅 14:00-15:00 IT Team Weekly | 📅 16:00-16:30 Call mit LANdata`
|
|
6. User selects → script returns event data as JSON
|
|
7. Templater renders note, runs participant matching, writes file to `03 Bereiche/Meetings/`
|
|
|
|
### Components
|
|
|
|
- `scripts/o365-calendar.js` — Node.js script, Graph API client, OAuth2 token management
|
|
- `scripts/person-matcher.js` — Reads person notes, matches by email/name, creates new person notes
|
|
- Templater template referencing both scripts
|
|
|
|
## Creation Path B: Via Claude (MCP)
|
|
|
|
1. User says "Leg Meeting-Notiz an" or similar
|
|
2. Claude calls MCP tools: `list-calendar-events` → find event → `get-event-details`
|
|
3. Claude applies same matching logic (persons, series, agenda)
|
|
4. Claude creates note in same format
|
|
|
|
### MCP Server
|
|
|
|
- **Server:** `softeria/graph-mcp-server` (or best available alternative at implementation time)
|
|
- **Config:** `~/.claude/` MCP server configuration
|
|
- **Auth:** Same Azure App Registration as Templater path
|
|
- **Scope:** Read-only calendar access
|
|
|
|
### Matching rules in CLAUDE.md
|
|
|
|
The matching rules (email → person, domain → company, title → series) are documented in CLAUDE.md so Claude applies them consistently without the Node.js scripts.
|
|
|
|
## Boundaries
|
|
|
|
### Meeting Notes vs. Daily Notes
|
|
|
|
- Daily Notes remain unchanged: daily log, decisions, short notes
|
|
- Meeting Notes are standalone documents in `03 Bereiche/Meetings/`
|
|
- Connection via wikilinks: Daily Note can link to meeting note
|
|
|
|
### Meeting Notes vs. Jour Fixe
|
|
|
|
- Jour Fixe remains the running agenda management (Offen/Postponed/Dauerläufer)
|
|
- Meeting Notes are per-instance protocols
|
|
- For Jour Fixe series: typically NO meeting note created, work directly in agenda
|
|
- If created: agenda imported from Jour Fixe, meeting note links back
|
|
|
|
### What does NOT happen
|
|
|
|
- No automatic background synchronization
|
|
- No email copies in vault (calendar only in Phase 1)
|
|
- No write-back to O365
|
|
- No automatic meeting note creation — always conscious decision
|
|
|
|
## Documentation
|
|
|
|
A user-facing documentation note will be created at `03 Bereiche/IT-Management/O365 Kalender Integration.md`. This note explains:
|
|
|
|
- What the integration does and how it works (non-technical overview)
|
|
- How to create a meeting note manually (Templater workflow, step by step)
|
|
- How to create a meeting note via Claude
|
|
- How participant matching works (email → person, auto-creation)
|
|
- How series recognition and agenda import work
|
|
- Where meeting notes are stored and how to find them
|
|
- Known limitations and planned future extensions (email integration)
|
|
|
|
This is a living document, updated as the integration evolves.
|
|
|
|
## Prerequisites
|
|
|
|
- [ ] Add `email` field to all existing person notes (6 KRAH employees)
|
|
- [ ] Add `domain: krah-gruppe.de` to KRAH company note
|
|
- [ ] OAuth2 flow: Application (Client Credentials) — app-only access to user's calendar
|
|
- [ ] Node.js available on system
|
|
- [ ] Evaluate Softeria MCP server compatibility at implementation time
|
|
- [ ] Create user documentation at `03 Bereiche/IT-Management/O365 Kalender Integration.md`
|
|
|
|
## Open Questions
|
|
|
|
- Exact OAuth2 token storage/refresh strategy for Templater Node.js script
|
|
- Softeria MCP server: verify it supports delegated auth and calendar event listing with sufficient detail (body, attendees, recurrence)
|
|
- Fallback if Softeria is insufficient: build minimal custom MCP server against Graph API
|