Skip to content
← Community packages

Microsoft 365 helpers for Outlook, Calendar, To Do, OneDrive, and Teams.

Browse files

  • Integrations
  • microsoft
  • graph
  • outlook
  • mail
  • calendar
  • todo
  • onedrive
  • teams
  • oauth
  • multi-account
License
MIT
Published
August 22, 2026
Pinned commit
25b6dd3
Rating
No ratings yet
Forks
0
Stars
0
Adaptation effort

README

@kody/microsoft

Intent

One Microsoft Graph helpers package for Kody agents: Outlook mail, Calendar, To Do, OneDrive, and Teams. Centralizes OAuth integration selection, Graph transport, dry-run guards for mutations, and 403/insufficient-scope errors that name the missing delegated permission and the next /connect/oauth step. There is no built-in Kody Microsoft OAuth app — every connection is bring-your-own Azure app registration.

Auth

OAuth only (Azure AD / Microsoft Entra ID app registration). Not an API key. Not a bot token. Do not paste Graph access tokens into chat or into /account/secrets/new.

Client ID and client secret go into the Kody connect form. Tokens are stored as the saved integration's access/refresh secrets.

Wrong path (do not use for Graph access tokens):

https://kody.codes/account/secrets/new?name=microsoftAccessToken&description=Do%20not%20use.%20%40kody/microsoft%20is%20OAuth-only.%20Connect%20at%20/connect/oauth%20instead.&allowedHosts=graph.microsoft.com&scope=user

Required hosts

  • graph.microsoft.com (Microsoft Graph)
  • login.microsoftonline.com (token endpoint; Kody includes the token host automatically)

Agent setup

Kody does not ship a platform Microsoft OAuth app. Register your own confidential web app, then connect it.

  1. Open Azure app registrationsNew registration.
  2. Name the app (for example Kody Microsoft Graph).
  3. Supported account types:
    • Accounts in any organizational directory and personal Microsoft accounts → tenant common (default in the connect URLs below).
    • Work/school only → tenant organizations, or a specific tenant ID.
    • Personal Microsoft accounts only → tenant consumers.
  4. Platform: Web. Redirect URI (exact match, no trailing slash): https://kody.codes/connect/oauth.
  5. Certificates & secretsNew client secret. Copy the Value once.
  6. API permissionsMicrosoft GraphDelegated:
    • Always: openid, profile, email, offline_access, User.Read (smoke test).
    • Outlook mail: Mail.Read, Mail.ReadWrite, Mail.Send.
    • Calendar: Calendars.Read, Calendars.ReadWrite.
    • To Do: Tasks.Read, Tasks.ReadWrite.
    • OneDrive: Files.Read, Files.ReadWrite.
    • Teams: Chat.Read, Chat.ReadWrite, ChatMessage.Send, Team.ReadBasic.All, Channel.ReadBasic.All, ChannelMessage.Read.All, ChannelMessage.Send. Do not hide these exports when a token is missing a scope.
  7. For work/school tenants, an admin may need to Grant admin consent for Teams/ChannelMessage.Read.All and some org-restricted mail/file permissions. Personal Microsoft accounts cannot use admin-only permissions.
  8. While signed in to Kody, open a prefilled connect URL, paste the Application (client) ID and client secret, then authorize.

Redirect URI to register: https://kody.codes/connect/oauth

Connect URLs

Smoke test (User.Read only):

https://kody.codes/connect/oauth?provider=microsoft&authorizeUrl=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fv2.0%2Fauthorize&tokenUrl=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fv2.0%2Ftoken&flow=confidential&pkce=true&scopes=openid+profile+email+offline_access+User.Read&allowedHosts=graph.microsoft.com&apiBaseUrl=https%3A%2F%2Fgraph.microsoft.com%2Fv1.0&dashboardUrl=https%3A%2F%2Fportal.azure.com%2F%23view%2FMicrosoft_AAD_RegisteredApps%2FApplicationsListBlade&extraAuthorizeParams=%7B%22prompt%22%3A%22consent%22%7D

All products in this package:

https://kody.codes/connect/oauth?provider=microsoft&authorizeUrl=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fv2.0%2Fauthorize&tokenUrl=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fv2.0%2Ftoken&flow=confidential&pkce=true&scopes=openid+profile+email+offline_access+User.Read+Mail.Read+Mail.ReadWrite+Mail.Send+Calendars.Read+Calendars.ReadWrite+Tasks.Read+Tasks.ReadWrite+Files.Read+Files.ReadWrite+Chat.Read+Chat.ReadWrite+ChatMessage.Send+Team.ReadBasic.All+Channel.ReadBasic.All+ChannelMessage.Read.All+ChannelMessage.Send&allowedHosts=graph.microsoft.com&apiBaseUrl=https%3A%2F%2Fgraph.microsoft.com%2Fv1.0&dashboardUrl=https%3A%2F%2Fportal.azure.com%2F%23view%2FMicrosoft_AAD_RegisteredApps%2FApplicationsListBlade&extraAuthorizeParams=%7B%22prompt%22%3A%22consent%22%7D

Outlook mail only:

https://kody.codes/connect/oauth?provider=microsoft&authorizeUrl=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fv2.0%2Fauthorize&tokenUrl=https%3A%2F%2Flogin.microsoftonline.com%2Fcommon%2Foauth2%2Fv2.0%2Ftoken&flow=confidential&pkce=true&scopes=openid+profile+email+offline_access+User.Read+Mail.Read+Mail.ReadWrite+Mail.Send&allowedHosts=graph.microsoft.com&apiBaseUrl=https%3A%2F%2Fgraph.microsoft.com%2Fv1.0&dashboardUrl=https%3A%2F%2Fportal.azure.com%2F%23view%2FMicrosoft_AAD_RegisteredApps%2FApplicationsListBlade&extraAuthorizeParams=%7B%22prompt%22%3A%22consent%22%7D

Reconnect an existing connection (scope menu / saved endpoints):

https://kody.codes/connect/oauth?provider=microsoft

A second Microsoft account uses a different provider name, for example microsoft-work. Pass that name as integration (or account: 'work') on every call.

Decoded connect settings:

  • Authorize: https://login.microsoftonline.com/common/oauth2/v2.0/authorize
  • Token: https://login.microsoftonline.com/common/oauth2/v2.0/token
  • Flow: confidential (client secret on the token request) with pkce=true
  • Extra authorize params: {"prompt":"consent"} so added scopes appear on reconnect
  • API base: https://graph.microsoft.com/v1.0

Multi-account

No hard-coded person or brand aliases. Resolution:

  • integration wins (exact Kody OAuth name, e.g. microsoft-work)
  • account: 'work'microsoft-work
  • account: 'default' / 'microsoft' / omitted → microsoft
  • values that already start with microsoft- are used as-is

Exports

  • kody:@kody/microsoft — overview and smokeTest() (GET /me)
  • kody:@kody/microsoft/smoke-test — same read-only smoke test
  • kody:@kody/microsoft/accounts — integration naming
  • kody:@kody/microsoft/scopes — delegated scope catalog and connect URLs
  • kody:@kody/microsoft/mail — list/read Outlook messages, folders, sendMail, createReplyDraft
  • kody:@kody/microsoft/outlook — same as ./mail
  • kody:@kody/microsoft/calendar — calendars, events, calendarView, create/update
  • kody:@kody/microsoft/todo — To Do lists and tasks
  • kody:@kody/microsoft/onedrive — drive metadata, children, search, item metadata
  • kody:@kody/microsoft/teams — joined teams, chats, chat messages, channels, channel messages, sendChannelMessage
  • kody:@kody/microsoft/request — generic Graph escape hatch
  • kody:@kody/microsoft/core — authenticated Graph transport and 403 scope errors
  • kody:@kody/microsoft/types — shared TypeScript types

Mutations (sendMail, createReplyDraft, createEvent, updateEvent, createTask, sendChatMessage, sendChannelMessage, and non-GET request) take dryRun: true to preview without calling Graph. Live writes also require confirm: true after explicit user approval of the exact change.

Smoke test

After connecting, invoke the read-only export (no mail send, no calendar write):

import { packages } from 'kody:runtime'

export default async function main() {
  return await packages.invoke({
    kodyId: 'microsoft',
    exportName: './smoke-test',
  })
}

A successful result looks like { ok: true, integration: 'microsoft', id, displayName, mail, userPrincipalName }.

If the integration is missing, the error names OAuth (not API key / bot token) and includes the /connect/oauth URL. If Graph returns 403, the error names User.Read and the reconnect URL.

Examples

import { listMessages } from 'kody:@kody/microsoft/mail'

export default async function main() {
  return await listMessages({ top: 5 })
}
import { sendMail } from 'kody:@kody/microsoft/mail'

export default async function main() {
  return await sendMail({
    subject: 'Status update',
    body: 'Shipped the Microsoft helpers package.',
    to: 'teammate@example.com',
    dryRun: true,
  })
}
import { listEvents } from 'kody:@kody/microsoft/calendar'

export default async function main() {
  return await listEvents({
    account: 'work',
    startDateTime: '2026-08-22T00:00:00Z',
    endDateTime: '2026-08-23T00:00:00Z',
  })
}

403 / insufficient scope

Helpers throw MicrosoftGraphError instead of a bare HTTP 403. The message includes:

  • the Graph path and saved integration name
  • the delegated scope Graph likely needs (for example Mail.Read for /me/messages)
  • whether admin consent is likely (Teams channel messages, some org tenants)
  • the next setup step: add the permission on the Azure app, then reconnect at the prefilled /connect/oauth URL

Changing Azure API permissions does not update the current access token. Reconnect after every scope change.

Scopes by export

ExportRead scopesWrite / send scopes
smoke-testUser.Read
mailMail.ReadMail.ReadWrite, Mail.Send
calendarCalendars.ReadCalendars.ReadWrite
todoTasks.ReadTasks.ReadWrite
onedriveFiles.ReadFiles.ReadWrite
teams chatsChat.ReadChat.ReadWrite, ChatMessage.Send
teams joined / channelsTeam.ReadBasic.All, Channel.ReadBasic.All
teams channel messagesChannelMessage.Read.All (often admin consent)ChannelMessage.Send
Report this listing

Log in to report this listing.