Files
Claude-SharepointLists/README.md
ang3l12 a4384655d0 README accuracy pass: fix mcp-remote header pattern, add skill install steps
The Desktop config examples showed an inline 'Authorization: Bearer ...' header, which Claude Desktop breaks by splitting args on spaces — replace with the verified env-var pattern (Authorization:${MCP_AUTH_HEADER}, no space). Also: document how the skill is installed/discovered in Claude Code, note the person command in the CLI summary, and correct /health description (it now reports the secret-expiry countdown).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 19:16:38 -06:00

7.8 KiB

Claude-SharepointLists

Connect Claude to Microsoft SharePoint Lists via the Microsoft Graph API, with full read/write (CRUD) over list items using app-only (client-credentials) authentication.

The repo ships the same capability in two forms, sharing one core Graph client (graph.mjs):

Form Best for Entry point
Skill Claude Code on your own machine .claude/skills/sharepoint-lists/
MCP server Claude Desktop, or self-hosted in Docker mcp-server/

Repo layout

.
├── .claude/skills/sharepoint-lists/     # the skill (Claude Code)
│   ├── SKILL.md                         # how the skill works + usage
│   ├── .env.example                     # credential template (.env is git-ignored)
│   ├── scripts/
│   │   ├── sp.mjs                       # CLI: test|lists|columns|items|get|person|create|update|delete
│   │   └── lib/graph.mjs                # shared, dependency-free Graph client (single source of truth)
│   └── references/
│       ├── setup.md                     # Azure AD (Entra) app registration walkthrough
│       └── graph-api.md                 # field-type formats + OData query reference
├── mcp-server/                          # the MCP server (imports the same graph.mjs)
│   ├── index.mjs                        # stdio + Streamable HTTP transports
│   ├── Dockerfile
│   ├── .env.example
│   └── README.md                        # full MCP server + hosting docs
├── docker-compose.yml                   # run the MCP server as a container
└── .mcp.json                            # registers the server for Claude Code

Prerequisites (one-time)

An Entra (Azure AD) app registration with an application Graph permission (Sites.Selected preferred over Sites.ReadWrite.All) and admin consent. The full walkthrough is in references/setup.md. You end up with three secrets: SP_TENANT_ID, SP_CLIENT_ID, SP_CLIENT_SECRET.

Credentials live only in git-ignored .env files — never commit secrets.


Option A — the skill (Claude Code)

Install: clone this repo and open it in Claude Code — skills under .claude/skills/ are discovered automatically at session start (invokable as /sharepoint-lists). To use the skill in a different project, copy the .claude/skills/sharepoint-lists/ folder into that project (or into ~/.claude/skills/ to make it available everywhere), then create its .env:

cp .claude/skills/sharepoint-lists/.env.example .claude/skills/sharepoint-lists/.env
# fill in SP_TENANT_ID / SP_CLIENT_ID / SP_CLIENT_SECRET (optionally SP_SITE_URL)

cd .claude/skills/sharepoint-lists/scripts
node sp.mjs test --site "https://<tenant>.sharepoint.com/sites/<SiteName>"
node sp.mjs items --site "<SITE_URL>" --list "Projects" --filter "fields/Status eq 'Open'"

See SKILL.md for the full command set.


Option B — the MCP server

Exposes the Lists as MCP tools. The Graph/auth logic is the same graph.mjs the skill uses, so there is one source of truth.

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_resolve_person read Email → the LookupId person columns store (for writes)
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.

Transports

Transport Use How the client connects
stdio (default) client and server on the same machine the app launches node index.mjs
Streamable HTTP (--http) server hosted elsewhere (e.g. Docker) client → POST /mcp with a bearer token

stdio can't cross machines (the client launches the server as a child process), so a containerized/remote deployment must use the HTTP transport.

Environment

Var Required Notes
SP_TENANT_ID / SP_CLIENT_ID / SP_CLIENT_SECRET yes from the app registration
SP_SITE_URL no default site so tools can omit the site argument
SP_SECRET_EXPIRES no client secret expiry (YYYY-MM-DD); warns via CLI//health/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

cd mcp-server
npm install
cp .env.example .env        # fill in credentials
npm start                   # stdio
npm run start:http          # Streamable HTTP on :3838

Host it in Docker (Compose)

Run from the repo root (the build context needs the shared graph.mjs):

# 1) create mcp-server/.env with SP_* creds + a strong 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 has restart: unless-stopped and a healthcheck. The port publishes on all host interfaces; restrict it to a LAN IP in docker-compose.yml (or via firewall) if desired — the bearer token guards /mcp regardless. /health is intentionally open (liveness + secret-expiry countdown only; no secrets).

Full hosting details: mcp-server/README.md.

Connect a client

Claude Code — the repo-root .mcp.json registers the local stdio server automatically.

Claude Desktop → local stdio (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "sharepoint-lists": {
      "command": "/opt/homebrew/bin/node",
      "args": ["/absolute/path/to/Claude-SharepointLists/mcp-server/index.mjs"]
    }
  }
}

Claude Desktop → remote container (bridges local stdio to the HTTP server with mcp-remote):

{
  "mcpServers": {
    "sharepoint-lists": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "http://<host>:3838/mcp",
        "--allow-http",
        "--header", "Authorization:${MCP_AUTH_HEADER}"
      ],
      "env": { "MCP_AUTH_HEADER": "Bearer <your MCP_AUTH_TOKEN>" }
    }
  }
}

--allow-http permits a plain-HTTP LAN URL; the token must match the container's MCP_AUTH_TOKEN. Note the header value is passed via the env var (including the Bearer prefix) and there is no space after Authorization: — Claude Desktop splits config args on spaces, so an inline Bearer <token> breaks. Fully quit and reopen Claude Desktop after editing the config.


Security notes

  • App-only auth has no per-user check — the server acts as the app and can reach every granted SharePoint site. Anyone who can call an unauthenticated /mcp gets that access, so always set MCP_AUTH_TOKEN for the HTTP transport and keep the endpoint on a trusted network (this is not meant for public web chat).
  • Secrets (SP_CLIENT_SECRET, MCP_AUTH_TOKEN) live only in git-ignored .env files and are never baked into the Docker image.
  • Field formats for writes (person/lookup/choice/date) are documented in references/graph-api.md.