brain/docs/superpowers/specs/2026-04-12-o365-calendar-in...

8.9 KiB

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

# New field added to all person notes
email: christopher.klein@krah.de  # Primary match key

Company frontmatter extension

# 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:

---
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

---
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