m365-query
otc-awesome-llm is the Optum LLM library providing version-controlled prompts, chatmodes, instructions, and agent modes for infrastructure operations via native IDE integrations
By Thomas Hudak ([email protected])
Plugin Structure
Installation
Install this plugin using the Claude Code CLI:
claude plugin install m365-query@otc-awesome-llmVerification
After installation, verify the plugin is loaded:
claude plugin listDocumentation
M365 Query Plugin
Unified Microsoft 365 content search across SharePoint, Teams,
Outlook, and OneDrive via the Microsoft Graph /search/query
endpoint. Sits on top of the shared ms_auth module and the
per-surface clients (SharePointClient, TeamsClient,
OutlookClient).
Capabilities
- Single
search()call fans the user's query out to multiple Graph search "verticals" with the rightentityTypesper surface - Returns hits grouped by source (
sharepoint,teams,outlook,onedrive) so callers can drill in via the per-surface clients - Per-source deep-link helpers for follow-up retrieval:
get_sharepoint_item(site_id, list_id, item_id)get_chat_message(chat_id, message_id)get_outlook_message(message_id)get_outlook_event(event_id)
- CLI for ad-hoc queries:
python m365_query.py search --query "X"
First-time auth
No .local.md setup is required for users in the UHG tenant — the
shipped defaults work out of the box. First call opens a browser
for SSO; subsequent calls within the token TTL are silent (cache
lives in the system keyring).
For other tenants, override via ~/.claude/ms-auth.local.md:
---
tenant_id: your-tenant-guid
client_id: your-app-id
---
Quickstart
import asyncio
from m365_query import M365Query
async def main() -> None:
q = M365Query()
results = await q.search(
"COIL",
sources=["sharepoint", "teams", "outlook"],
top=10,
)
for source, hits in results.items():
print(source, len(hits))
await q.close()
asyncio.run(main())
CLI
# All four sources, top 25 each, summary view
python m365_query.py search --query "COIL"
# Subset, JSON output
python m365_query.py search --query "firewall automation" \
--sources teams,outlook --json
# Clear cached tokens
python m365_query.py logout
Triggers (skill activation)
The skill activates when the user asks for cross-surface M365 content discovery. Triggers:
- "search across my microsoft tools for X"
- "find anything about COIL in m365"
- "cross-channel search teams and outlook for Y"
- "graph search for Z"
- "look across sharepoint and teams for X"
- "search my m365 content"
Default scopes
Search.Read.All
Sites.Read.All, Files.Read.All
Chat.Read, Chat.ReadBasic, ChannelMessage.Read.All,
Channel.ReadBasic.All, Team.ReadBasic.All, Group.Read.All
Mail.Read, Mail.ReadBasic, Calendars.Read, Contacts.Read,
MailboxSettings.Read
User.Read
UHG admin-consent status for these scopes
The Microsoft Graph Command Line Tools appId carries ~33 tenant-wide
delegated grants on its service principal (84dd5a71-61b8-41b7-8f0f-92fbe43075bc)
in the UHG tenant.
Admin-consented (work today):
Search.Read.All, Sites.Read.All, Files.Read.All,
Files.ReadWrite.All, Group.Read.All, Group.ReadWrite.All,
User.Read.All, Directory.Read.All, Application.Read.All,
Channel.ReadBasic.All, Team.ReadBasic.All,
SharePointTenantSettings.Read.All, plus ~20 others (full list in
KNOWN_GOOD_SCOPES in shared/ms_auth/config.py).
NOT admin-consented (pending — call 403s today):
Mail.Read, Mail.ReadBasic, Calendars.Read, Chat.Read,
ChannelMessage.Read.All, Contacts.Read, MailboxSettings.Read.
Admin-consent requests for the missing scopes are in flight. The
Teams scopes (Chat.Read, ChannelMessage.Read.All) have a
working unblock today via the Legion-stored token in
teams-automation; the Outlook scopes do not (Legion's appId
doesn't include them either).
Override the appId or tenant via ~/.claude/m365-query.local.md or
~/.claude/ms-auth.local.md if you need a different set.
Verified 2026-05-11 via
oauth2PermissionGrantsquery on service principal84dd5a71-61b8-41b7-8f0f-92fbe43075bc.
Read-only
This plugin has no write paths — /search/query is GET-equivalent
and all deep-link helpers are client.get(...). Future PRs will
add the write-mode counterparts (Mail.Send, Chat.Send,
event create) under the existing read_only_mode gate used by
the other automation plugins.
File layout
m365-query/
.claude-plugin/
plugin.json # auto-generated from plugin.json.j2
plugin.json.j2 # template (edit this)
skills/
m365-query/
skill.md # skill description + triggers
scripts/
m365_query.py # M365Query class + CLI
pyproject.toml
requirements.txt
README.md

