Skip to content

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

v11.3.0
Claude Code

By Thomas Hudak ([email protected])

Plugin Structure

🤖
0
Agents
0
Skills
⌨️
0
Commands
🪝
0
Hooks
📋
0
Rules

Installation

Install this plugin using the Claude Code CLI:

claude plugin install m365-query@otc-awesome-llm

Verification

After installation, verify the plugin is loaded:

claude plugin list

Documentation

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 right entityTypes per 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 oauth2PermissionGrants query on service principal 84dd5a71-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