Azure client secrets lapse silently into opaque 401s (AADSTS7000222). Add a self-reported expiry date (SP_SECRET_EXPIRES=YYYY-MM-DD) and a shared secretExpiryStatus() helper in graph.mjs; surface warnings <30 days out via the CLI (stderr on every command + test output), MCP server startup log, /health, and the sharepoint_test tool. Documented in both .env.examples, setup.md, and READMEs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
129 lines
4.3 KiB
Markdown
129 lines
4.3 KiB
Markdown
# sharepoint-lists MCP server
|
|
|
|
An [MCP](https://modelcontextprotocol.io) server exposing Microsoft SharePoint
|
|
Lists as tools, using app-only (client-credentials) Microsoft Graph auth. It
|
|
reuses the same core client as the skill
|
|
(`../.claude/skills/sharepoint-lists/scripts/lib/graph.mjs`) — one source of
|
|
truth for the Graph logic.
|
|
|
|
## Transports
|
|
|
|
| Transport | Use | How the client connects |
|
|
|---|---|---|
|
|
| **stdio** (default) | local Claude apps on the same machine | the app launches `node index.mjs` |
|
|
| **Streamable HTTP** (`--http`) | hosted in a container, reached over the network | client → HTTP `POST /mcp` (bearer token) |
|
|
|
|
stdio is for same-machine use. To run as a hosted service (e.g. Docker on
|
|
another box), use the HTTP transport — a stdio client can't attach to a process
|
|
on a different machine.
|
|
|
|
## Tools
|
|
|
|
| Tool | Access | Purpose |
|
|
|---|---|---|
|
|
| `sharepoint_test` | read | Verify credentials; with a site, list its lists |
|
|
| `sharepoint_list_lists` | read | Enumerate lists in a site |
|
|
| `sharepoint_get_columns` | read | Column internal names + types (call before writing) |
|
|
| `sharepoint_list_items` | read | Query items (filter/select/orderby/top/all) |
|
|
| `sharepoint_get_item` | read | Get one item by id |
|
|
| `sharepoint_create_item` | write | Create an item |
|
|
| `sharepoint_update_item` | write | Update an item (partial) |
|
|
| `sharepoint_delete_item` | write | Delete an item (irreversible) |
|
|
|
|
Set `SP_READONLY=true` to register only the read tools.
|
|
|
|
## Configuration
|
|
|
|
Credentials come from the environment or `mcp-server/.env` (git-ignored). See
|
|
`.env.example` and `../.claude/skills/sharepoint-lists/references/setup.md`.
|
|
|
|
| Var | Required | Notes |
|
|
|---|---|---|
|
|
| `SP_TENANT_ID` / `SP_CLIENT_ID` / `SP_CLIENT_SECRET` | yes | app registration |
|
|
| `SP_SITE_URL` | no | default site so tools can omit `site` |
|
|
| `SP_SECRET_EXPIRES` | no | client secret's expiry date (`YYYY-MM-DD`); warns at startup, in `/health`, and in `sharepoint_test` when <30 days remain |
|
|
| `SP_READONLY` | no | `true` → read-only tool set |
|
|
| `MCP_AUTH_TOKEN` | HTTP only | shared bearer token required on `/mcp`; empty = unauthenticated |
|
|
| `PORT` | HTTP only | listen port (default 3838) |
|
|
|
|
## Run locally
|
|
|
|
```bash
|
|
cd mcp-server
|
|
npm install
|
|
cp .env.example .env # fill in credentials
|
|
npm start # stdio
|
|
npm run start:http # Streamable HTTP on :3838
|
|
```
|
|
|
|
## Run as a container (Docker Compose)
|
|
|
|
From the **repo root** (the build context needs the shared `graph.mjs`):
|
|
|
|
```bash
|
|
# 1. Create mcp-server/.env with SP_* creds + a token:
|
|
# echo "MCP_AUTH_TOKEN=$(openssl rand -hex 32)" >> mcp-server/.env
|
|
# 2. Build + start:
|
|
docker compose up -d --build
|
|
# 3. Check:
|
|
curl -s http://localhost:3838/health # {"ok":true,...}
|
|
```
|
|
|
|
The service restarts automatically and has a healthcheck. The port is published
|
|
on all interfaces; restrict it in `docker-compose.yml` (or via firewall) and rely
|
|
on `MCP_AUTH_TOKEN` for auth.
|
|
|
|
## Connect a client
|
|
|
|
### Claude Desktop → remote HTTP server (via `mcp-remote`)
|
|
|
|
Desktop speaks stdio, so bridge to the remote HTTP server with `mcp-remote` in
|
|
`~/Library/Application Support/Claude/claude_desktop_config.json`, then restart
|
|
Desktop:
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"sharepoint-lists": {
|
|
"command": "npx",
|
|
"args": [
|
|
"-y", "mcp-remote",
|
|
"http://192.168.101.12:3838/mcp",
|
|
"--allow-http",
|
|
"--header", "Authorization: Bearer ${SP_MCP_TOKEN}"
|
|
],
|
|
"env": { "SP_MCP_TOKEN": "<your MCP_AUTH_TOKEN>" }
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
`--allow-http` permits the plain-HTTP LAN URL (no TLS). The token must match the
|
|
container's `MCP_AUTH_TOKEN`.
|
|
|
|
### Claude Desktop → local stdio (no container)
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"sharepoint-lists": {
|
|
"command": "/opt/homebrew/bin/node",
|
|
"args": ["/absolute/path/to/Claude-SharepointLists/mcp-server/index.mjs"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Claude Code
|
|
|
|
The repo-root `.mcp.json` registers the local stdio server automatically.
|
|
|
|
## Security notes
|
|
|
|
- App-only auth = the server has full access to the granted SharePoint sites
|
|
with no per-user check. Anyone who can reach an unauthenticated `/mcp` has that
|
|
access — always set `MCP_AUTH_TOKEN` for the HTTP transport.
|
|
- `/health` is intentionally unauthenticated (liveness only; returns no data).
|
|
- Field formats for writes (person/lookup/choice/date) are in
|
|
`../.claude/skills/sharepoint-lists/references/graph-api.md`.
|