Microsoft 365 helpers for Outlook, Calendar, To Do, OneDrive, and Teams.
- Integrations
- microsoft
- graph
- outlook
- 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):
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.
- Open Azure app registrations → New registration.
- Name the app (for example
Kody Microsoft Graph). - 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.
- Accounts in any organizational directory and personal Microsoft accounts → tenant
- Platform: Web. Redirect URI (exact match, no trailing slash):
https://kody.codes/connect/oauth. - Certificates & secrets → New client secret. Copy the Value once.
- API permissions → Microsoft Graph → Delegated:
- 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.
- Always:
- For work/school tenants, an admin may need to Grant admin consent for Teams/
ChannelMessage.Read.Alland some org-restricted mail/file permissions. Personal Microsoft accounts cannot use admin-only permissions. - 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):
All products in this package:
Outlook mail only:
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) withpkce=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:
integrationwins (exact Kody OAuth name, e.g.microsoft-work)account: 'work'→microsoft-workaccount: 'default'/'microsoft'/ omitted →microsoft- values that already start with
microsoft-are used as-is
Exports
kody:@kody/microsoft— overview andsmokeTest()(GET /me)kody:@kody/microsoft/smoke-test— same read-only smoke testkody:@kody/microsoft/accounts— integration namingkody:@kody/microsoft/scopes— delegated scope catalog and connect URLskody:@kody/microsoft/mail— list/read Outlook messages, folders,sendMail,createReplyDraftkody:@kody/microsoft/outlook— same as./mailkody:@kody/microsoft/calendar— calendars, events, calendarView, create/updatekody:@kody/microsoft/todo— To Do lists and taskskody:@kody/microsoft/onedrive— drive metadata, children, search, item metadatakody:@kody/microsoft/teams— joined teams, chats, chat messages, channels, channel messages,sendChannelMessagekody:@kody/microsoft/request— generic Graph escape hatchkody:@kody/microsoft/core— authenticated Graph transport and 403 scope errorskody:@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.Readfor/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/oauthURL
Changing Azure API permissions does not update the current access token. Reconnect after every scope change.
Scopes by export
| Export | Read scopes | Write / send scopes |
|---|---|---|
smoke-test | User.Read | — |
mail | Mail.Read | Mail.ReadWrite, Mail.Send |
calendar | Calendars.Read | Calendars.ReadWrite |
todo | Tasks.Read | Tasks.ReadWrite |
onedrive | Files.Read | Files.ReadWrite |
teams chats | Chat.Read | Chat.ReadWrite, ChatMessage.Send |
teams joined / channels | Team.ReadBasic.All, Channel.ReadBasic.All | — |
teams channel messages | ChannelMessage.Read.All (often admin consent) | ChannelMessage.Send |
Report this listing
Log in to report this listing.