--- name: sharepoint-lists description: >- Read and write Microsoft SharePoint Lists via the Microsoft Graph API using app-only (client-credentials) authentication. Use this skill whenever the user wants to query, search, list, create, add, update, edit, or delete items in a SharePoint List, inspect a list's columns, or enumerate the lists in a SharePoint site — even if they just refer to "our SharePoint", "the list", "the tracker", or a specific list/site name rather than saying "SharePoint List" explicitly. Also use it for bulk operations over list items (export, reporting, batch updates) and for connectivity/credential troubleshooting against SharePoint. This is for SharePoint *Lists* (structured rows/columns), not document libraries or files — for documents use the SharePoint search connector instead. --- # SharePoint Lists Connect to Microsoft SharePoint Lists and perform full CRUD (create, read, update, delete) on list items through the Microsoft Graph API. Authentication is **app-only** (OAuth2 client credentials) — no interactive login. All operations go through one CLI, `scripts/sp.mjs`, which wraps the reusable core client in `scripts/lib/graph.mjs`. Run it with Node (already available): ```bash node scripts/sp.mjs [flags] ``` > Paths above are relative to this skill's directory. From elsewhere, use the > absolute path, e.g. `node "/scripts/sp.mjs" ...`. ## Prerequisites (one-time) The skill needs three credentials from an Azure AD (Entra) **app registration** with an *application* permission for SharePoint and admin consent granted: - `SP_TENANT_ID` — Directory (tenant) ID - `SP_CLIENT_ID` — Application (client) ID - `SP_CLIENT_SECRET` — a client secret value Set them as environment variables, or copy `.env.example` to `.env` in this skill's directory and fill it in (the CLI loads `.env` automatically; `.env` is git-ignored). Optionally set `SP_SITE_URL` to a default site so `--site` can be omitted. **If credentials are not yet set up**, do not guess — point the user to [references/setup.md](references/setup.md), which is a step-by-step walkthrough of creating the app registration, choosing least-privilege permissions (`Sites.Selected` is strongly preferred over `Sites.ReadWrite.All`), and granting consent. Then come back and run `test`. ## First step in any task: verify connectivity Before doing real work, confirm auth works. This fails fast with a clear message if credentials or permissions are wrong: ```bash node scripts/sp.mjs test --site "https://contoso.sharepoint.com/sites/Marketing" ``` A successful response includes the resolved site id and the names of the lists in that site. ## Identifying a site and a list - **Site** is given by URL: `https://{tenant}.sharepoint.com/sites/{SiteName}`. The client resolves it to a Graph site id for you. A bare hostname means the root site. - **List** can be given by display name (e.g. `"Project Tracker"`), internal name, or GUID — the client resolves names to ids and, on a typo, lists the available names so you can correct it. When unsure what lists or columns exist, discover first rather than guessing: ```bash node scripts/sp.mjs lists --site "" node scripts/sp.mjs columns --site "" --list "Project Tracker" ``` `columns` is important before writing: SharePoint **internal** column names often differ from their display names (a column shown as "Due Date" may be `DueDate` or even `OData__x0044_ue...`). Always write to internal names. See [references/graph-api.md](references/graph-api.md) for field-type formats (person, lookup, choice, date, etc.) and OData query details. To write a **person column**, first resolve the person's email to the numeric LookupId the column stores, then write `LookupId`: ```bash node scripts/sp.mjs person --site "" --email user@domain.com # → {"lookupId": 53, ...}; then: --fields '{"ProjectManagerLookupId": 53}' ``` ## Reading items ```bash # Everything (follows pagination): node scripts/sp.mjs items --site "" --list "Project Tracker" --all # Filtered + sorted + projected: node scripts/sp.mjs items --site "" --list "Project Tracker" \ --filter "fields/Status eq 'Open'" \ --select "Title,Status,AssignedTo" \ --orderby "fields/DueDate asc" --top 50 # One item by id: node scripts/sp.mjs get --site "" --list "Project Tracker" --id 42 ``` Items come back simplified as `{ id, webUrl, createdDateTime, lastModifiedDateTime, fields }`. ## Writing items `--fields` takes a JSON object of internal column name → value. It also accepts `@file.json` or `-` (read JSON from stdin), which is cleaner for large or quote-heavy payloads. ```bash # Create node scripts/sp.mjs create --site "" --list "Project Tracker" \ --fields '{"Title":"Migrate database","Status":"Open","Priority":2}' # Update (partial — only the fields you pass change) node scripts/sp.mjs update --site "" --list "Project Tracker" \ --id 42 --fields '{"Status":"Done"}' # Delete node scripts/sp.mjs delete --site "" --list "Project Tracker" --id 42 ``` **Deletes are irreversible (items go to the site Recycle Bin). Confirm the item id with the user before deleting**, and prefer showing them the item via `get` first. For bulk deletes, confirm the full set once, then proceed. ## Bulk operations For exports or batch edits, fetch with `--all`, transform the JSON however the task needs (jq, a quick script), then loop the relevant `create`/`update`/ `delete` calls. Keep concurrency modest — Graph throttles with HTTP 429; the client already retries with backoff, but don't fan out hundreds of parallel writes. ## Handling errors The CLI prints a JSON error to stderr and exits non-zero. Common cases: - `401` / token failure → bad client id/secret or tenant; re-check `.env`. - `403` (`accessDenied`) → the app lacks permission on this site. With `Sites.Selected`, the site must be explicitly granted to the app — see [references/setup.md](references/setup.md). - `404` on a list → wrong name; run `lists` to see valid names. - `400` on write → usually a wrong internal field name or value format; run `columns` and check [references/graph-api.md](references/graph-api.md). ## Extending to an MCP server The core logic is isolated in `scripts/lib/graph.mjs` (the `SharePointListsClient` class, zero dependencies). To graduate this into a standalone MCP server later, import that class and expose its methods (`listItems`, `getItem`, `createItem`, `updateItem`, `deleteItem`, etc.) as MCP tools — no rewrite of the Graph logic needed. See the note at the bottom of [references/setup.md](references/setup.md).