ms365-mcp
Provides a minimal Microsoft 365 MCP server with fourteen hand-written tools for email, people, and drafts, designed to fit small local models.
README
ms365-mcp
A Microsoft 365 MCP server sized for small local models.
Most MCP servers are built for frontier models with large context windows, so they expose everything the vendor API offers and let the model sort it out. The reference Microsoft 365 server exposes 326 tools generated from Microsoft's Graph OpenAPI spec.
That does not work for an 8B model at 8k context, where the entire tool list sits in the system prompt and the model starts choosing at random somewhere past twenty-five tools. This server takes the opposite approach:
- Fourteen hand-written tools, with a hard ceiling of twenty. The tool list is a budget, not a feature list.
- Descriptions written for a small model, not salvaged from API metadata.
- Draft-first. Exactly one tool can transmit to another person, it takes a draft id rather than a recipient, and it is annotated destructive.
- Every tool declares its own effect through MCP
readOnlyHintanddestructiveHintannotations, so a host can classify it without guessing from the tool's name.
Built for the Edge Harness, and useful to any MCP client.
Status
Feature complete, not yet run against a real account. All fourteen tools are implemented and 171 tests pass, none of which touch the network. What has not happened is the part only a real tenant can prove: a live sign-in and a Graph call that gets answered. See docs/PLAN.md for the ten commits and the reasoning behind each decision.
| Scaffold, licence, CI | done |
| MCP protocol over stdio | done |
| The fourteen-tool surface | done, all fourteen implemented |
| Sign-in | done, untested against a live tenant |
| Microsoft Graph | done, tested against recorded shapes only |
| Retries and circuit breaker | done |
Seeing the tool surface
--list-tools starts nothing and needs no account, so it answers "what would
a host be offered?" before any of it is wired up:
.venv/bin/python -m ms365_mcp --list-tools
Add --json for a machine-readable version.
read whoami Return the display name, email address and timezone…
read resolve_person Look up a person by name or partial name and return…
read list_messages List messages in a mail folder, newest first, as se…
write create_draft Create a draft message. This does NOT send anything…
! write send_draft Send a draft that already exists, by its id. This r…
...
14 tools, 6 that change something, 4 marked destructive (!)
Handlers are stubs today: a call returns a sentence saying the tool is declared but not implemented. The surface is deliberately fixed before any of it is built, because the tool names, descriptions and effect classes are what a host classifies and a model is prompted against, and those are expensive to change later.
Signing in
Sign-in is the Microsoft device code flow, so there is no redirect URL to host and no secret to store. You need one thing first: the application id of an Azure app registration that has the delegated Graph permissions this server's tools declare.
export MS365_MCP_CLIENT_ID=<your app registration's application id>
export MS365_MCP_TENANT_ID=organizations # 'consumers' for outlook.com
.venv/bin/python -m ms365_mcp --login
Then check it, before wiring anything into a host:
.venv/bin/python -m ms365_mcp --verify-login
--verify-login prints the client id, tenant, authority, scopes and cache
location, and if an account is cached it calls Graph once. That last part is
the point. A cached token can look completely healthy and still be refused,
because a work or school tenant can withhold consent for a scope the surface
needs, and that only ever shows up as a 403 on a real request. --logout
forgets the account and removes the cache.
The token cache lives in the operating system's config directory, never in this repository. It is created with mode 0600 rather than written and then made private, since the gap between those two is a window where a refresh token is readable by every account on the machine. It is replaced by an atomic rename, so an interrupted write costs nothing.
Using it from the Edge Harness
The harness launches this server as a subprocess over stdio, so it runs under
its own interpreter and shares no dependencies with the host. That is not
incidental: the harness has a local package called mcp/ and the official SDK
on PyPI is also called mcp, so installing this server into the harness's
virtualenv shadows the harness's own package.
Its registry entry there is ms365-own, and pressing Check in the Agent
Lab run options lists every tool with its effect class. All fourteen classify
as declared, meaning the harness took them from this server's annotations
rather than guessing from tool names, and the entry needs no overrides.
In the harness's default draft mode, send_draft is withheld and the other
thirteen are available, so a run cannot reach a person.
Errors are raised as the SDK's ToolError rather than allowed to propagate.
That is worth knowing if you are writing a handler here: the SDK treats any
other exception as a crash and replaces its message with a generic one, so a
sentence like "No signed-in account, run --login" is discarded before it ever
reaches the model. ToolError is the deliberate-failure channel and its text
survives, while the result is still flagged as an error.
Security
This repository is public and holds no credential of any kind.
Authentication uses the Microsoft device-code flow. The resulting token cache
is written to the operating system's config directory with owner-only
permissions, never into this repository or its working tree. .gitignore
covers token and cache filenames anyway, and a pre-commit hook scans staged
changes for credential-shaped strings, so committing one takes deliberate
effort rather than a slip.
A line ending in # pragma: allowlist secret is skipped by the hook. That
exists so this repository's own tests can contain fake credentials, and it
leaves a marker a reviewer can see in the diff, which --no-verify does not.
Use it only for data that is provably fake.
Development
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest -q
Enable the secret-scan hook once per clone:
git config core.hooksPath .githooks
Licence
MIT. See LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。