{"_id":"@emin-bit/pnp-mcp","_rev":"5-0e2f60a164c075dbea2f33e32723771f","name":"@emin-bit/pnp-mcp","dist-tags":{"latest":"1.2.0"},"versions":{"1.0.0":{"name":"@emin-bit/pnp-mcp","version":"1.0.0","keywords":["mcp","model-context-protocol","claude","claude-desktop","anthropic","pnp","pnp-powershell","sharepoint","sharepoint-online","microsoft-365","m365","powershell","pwsh","office-365","tenant-admin","provisioning"],"author":{"name":"Emin Mujabašić"},"license":"MIT","_id":"@emin-bit/pnp-mcp@1.0.0","maintainers":[{"name":"emin-bit","email":"emin.mujabasic@gmail.com"}],"homepage":"https://github.com/Emin-bit/pnp-mcp#readme","bugs":{"url":"https://github.com/Emin-bit/pnp-mcp/issues"},"bin":{"pnp-mcp":"dist/index.js"},"dist":{"shasum":"782175dc4aeff1babc9e16ac11d310ed908be735","tarball":"https://registry.npmjs.org/@emin-bit/pnp-mcp/-/pnp-mcp-1.0.0.tgz","fileCount":36,"integrity":"sha512-tfuIfh+DLXw44XdSvcRGmRbpUdA217mptLTzlUhJKFsXFn8WbvVq3d2hAXAOqHWx2q2blK9ONiGkrbrxa5Dcpg==","signatures":[{"sig":"MEUCIFPvoU6uMlTWfGrIoLITTALTz3RzubYvtW1u3CIDGsV7AiEAoTd16ybx4fhihRAmnDksc8Zf+AUzjlKHhtRYmyGYiYc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":243135},"main":"dist/index.js","type":"module","engines":{"node":">=18"},"gitHead":"f9b3623c2786ff701599705f4ed1aba0f2d98c11","scripts":{"dev":"tsc --watch","test":"npm run build && node smoke-test.mjs","build":"tsc && chmod +x dist/index.js","clean":"rm -rf dist","start":"node dist/index.js","verify-enums":"node verify-enums.mjs","prepublishOnly":"npm run clean && npm run test && npm run verify-enums"},"_npmUser":{"name":"emin-bit","email":"emin.mujabasic@gmail.com"},"repository":{"url":"git+https://github.com/Emin-bit/pnp-mcp.git","type":"git"},"_npmVersion":"10.9.2","description":"MCP (Model Context Protocol) server bridging Claude with the PnP PowerShell module for SharePoint Online and Microsoft 365 administration. Wraps `pwsh` 7+ with PnP.PowerShell module installed; exposes auth lifecycle, sites/webs/lists/files/content-types/p","directories":{},"_nodeVersion":"22.14.0","dependencies":{"zod":"^3.23.8","@modelcontextprotocol/sdk":"^1.0.4"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.3","@types/node":"^22.10.0"},"_npmOperationalInternal":{"tmp":"tmp/pnp-mcp_1.0.0_1777891828608_0.008603819145367897","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@emin-bit/pnp-mcp","version":"1.0.1","keywords":["mcp","model-context-protocol","claude","claude-desktop","anthropic","pnp","pnp-powershell","sharepoint","sharepoint-online","microsoft-365","m365","powershell","pwsh","office-365","tenant-admin","provisioning"],"author":{"name":"Emin Mujabašić"},"license":"MIT","_id":"@emin-bit/pnp-mcp@1.0.1","maintainers":[{"name":"emin-bit","email":"emin.mujabasic@gmail.com"}],"homepage":"https://github.com/Emin-bit/pnp-mcp#readme","bugs":{"url":"https://github.com/Emin-bit/pnp-mcp/issues"},"bin":{"pnp-mcp":"dist/index.js"},"dist":{"shasum":"f9386458b54644de9eaa18f8e960b284cf67e4ac","tarball":"https://registry.npmjs.org/@emin-bit/pnp-mcp/-/pnp-mcp-1.0.1.tgz","fileCount":36,"integrity":"sha512-K+PA/5xVWEYUHeTWnzplj2VnCdFRGH89e2VSzjLSS4pSTmP2J/qqD5JTSrB+h6Cu+0wRGrBzalMlBA5NTR5L0g==","signatures":[{"sig":"MEQCIAjHNvxYaQCtQKRX69zbzm144DdLI3f3zM5FcGu0Daj8AiBaJoT7XOJIT7ruMqogJw3o9fzR6HV/gb47RSFTKZQZwQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":253128},"main":"dist/index.js","type":"module","engines":{"node":">=18"},"gitHead":"62f738268508a9dba1a99777b719258fe086a1f4","scripts":{"dev":"tsc --watch","test":"npm run build && node smoke-test.mjs","build":"tsc && chmod +x dist/index.js","clean":"rm -rf dist","start":"node dist/index.js","verify-enums":"node verify-enums.mjs","prepublishOnly":"npm run clean && npm run test && npm run verify-enums"},"_npmUser":{"name":"emin-bit","email":"emin.mujabasic@gmail.com"},"repository":{"url":"git+https://github.com/Emin-bit/pnp-mcp.git","type":"git"},"_npmVersion":"10.9.2","description":"MCP (Model Context Protocol) server bridging Claude with the PnP PowerShell module for SharePoint Online and Microsoft 365 administration. Wraps `pwsh` 7+ with PnP.PowerShell module installed; exposes auth lifecycle, sites/webs/lists/files/content-types/p","directories":{},"_nodeVersion":"22.14.0","dependencies":{"zod":"^3.23.8","update-notifier":"^7.3.1","@modelcontextprotocol/sdk":"^1.0.4"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.3","@types/node":"^22.10.0","@types/update-notifier":"^6.0.8"},"_npmOperationalInternal":{"tmp":"tmp/pnp-mcp_1.0.1_1778352227840_0.08850165015349387","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@emin-bit/pnp-mcp","version":"1.1.0","keywords":["mcp","model-context-protocol","claude","claude-desktop","anthropic","pnp","pnp-powershell","sharepoint","sharepoint-online","microsoft-365","m365","powershell","pwsh","office-365","tenant-admin","provisioning"],"author":{"name":"Emin Mujabašić"},"license":"MIT","_id":"@emin-bit/pnp-mcp@1.1.0","maintainers":[{"name":"emin-bit","email":"emin.mujabasic@gmail.com"}],"homepage":"https://github.com/Emin-bit/pnp-mcp#readme","bugs":{"url":"https://github.com/Emin-bit/pnp-mcp/issues"},"bin":{"pnp-mcp":"dist/index.js"},"dist":{"shasum":"c7065b58e19cdf6d4c32cb4f1403a55cc260c2f1","tarball":"https://registry.npmjs.org/@emin-bit/pnp-mcp/-/pnp-mcp-1.1.0.tgz","fileCount":38,"integrity":"sha512-Dey1POoZdcBUBwvlzA5R31pAyhLpycrEigH0DxtBSvZYINKnozFmAMWQPj52G5rogOUXrjRhrnWkC4KdrsNd8Q==","signatures":[{"sig":"MEUCIDIuoPawdNLxvZNANB3crwKRq3r/x1rhHqlhAEZXuMOyAiEAjznB0pRHDr7A85c5ijIT4CC7p2F240V/NtaSg8WnPqg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":278200},"main":"dist/index.js","type":"module","engines":{"node":">=18"},"gitHead":"59ba114658f0127b29887c17575ebe1bc723a364","scripts":{"dev":"tsc --watch","test":"npm run build && node smoke-test.mjs","build":"tsc && chmod +x dist/index.js","clean":"rm -rf dist","start":"node dist/index.js","verify-enums":"node verify-enums.mjs","prepublishOnly":"npm run clean && npm run test && npm run verify-enums"},"_npmUser":{"name":"emin-bit","email":"emin.mujabasic@gmail.com"},"repository":{"url":"git+https://github.com/Emin-bit/pnp-mcp.git","type":"git"},"_npmVersion":"10.9.2","description":"MCP (Model Context Protocol) server bridging Claude with the PnP PowerShell module for SharePoint Online and Microsoft 365 administration. Wraps `pwsh` 7+ with PnP.PowerShell module installed; exposes auth lifecycle, sites/webs/lists/files/content-types/p","directories":{},"_nodeVersion":"22.14.0","dependencies":{"zod":"^3.23.8","update-notifier":"^7.3.1","@modelcontextprotocol/sdk":"^1.0.4"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.3","@types/node":"^22.10.0","@types/update-notifier":"^6.0.8"},"_npmOperationalInternal":{"tmp":"tmp/pnp-mcp_1.1.0_1778438808415_0.7765630741826295","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@emin-bit/pnp-mcp","version":"1.1.1","keywords":["mcp","model-context-protocol","claude","claude-desktop","anthropic","pnp","pnp-powershell","sharepoint","sharepoint-online","microsoft-365","m365","powershell","pwsh","office-365","tenant-admin","provisioning"],"author":{"name":"Emin Mujabašić"},"license":"MIT","_id":"@emin-bit/pnp-mcp@1.1.1","maintainers":[{"name":"emin-bit","email":"emin.mujabasic@gmail.com"}],"homepage":"https://github.com/Emin-bit/pnp-mcp#readme","bugs":{"url":"https://github.com/Emin-bit/pnp-mcp/issues"},"bin":{"pnp-mcp":"dist/index.js"},"dist":{"shasum":"ff3fea9ee635ad905afdc37d724c0a9e14f26b46","tarball":"https://registry.npmjs.org/@emin-bit/pnp-mcp/-/pnp-mcp-1.1.1.tgz","fileCount":38,"integrity":"sha512-lRhUL3wj9WZtjcLR5805IggNU8uKJs2kiuiSfzHGv0d291H3qwlS0gVCHKdBpFXkr32iEjPi5AHvZKK+uZZbbw==","signatures":[{"sig":"MEUCIGvYY1TlufnZOYi2wq46TXIoIOvo5AlQaNhueBcE0I8nAiEAiLBpB99//Q4us4CWEi8qEd+osxvm3QP/upldisEOZvw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":279112},"main":"dist/index.js","type":"module","engines":{"node":">=18"},"gitHead":"23f99b5255fe082424825c93eea6b936f302779e","scripts":{"dev":"tsc --watch","test":"npm run build && node smoke-test.mjs","build":"tsc && chmod +x dist/index.js","clean":"rm -rf dist","start":"node dist/index.js","verify-enums":"node verify-enums.mjs","prepublishOnly":"npm run clean && npm run test && npm run verify-enums"},"_npmUser":{"name":"emin-bit","email":"emin.mujabasic@gmail.com"},"repository":{"url":"git+https://github.com/Emin-bit/pnp-mcp.git","type":"git"},"_npmVersion":"10.9.2","description":"MCP (Model Context Protocol) server bridging Claude with the PnP PowerShell module for SharePoint Online and Microsoft 365 administration. Wraps `pwsh` 7+ with PnP.PowerShell module installed; exposes auth lifecycle, sites/webs/lists/files/content-types/p","directories":{},"_nodeVersion":"22.14.0","dependencies":{"zod":"^3.23.8","semver":"^7.6.3","update-notifier":"^7.3.1","@modelcontextprotocol/sdk":"^1.0.4"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.3","@types/node":"^22.10.0","@types/semver":"^7.5.8","@types/update-notifier":"^6.0.8"},"_npmOperationalInternal":{"tmp":"tmp/pnp-mcp_1.1.1_1778484133082_0.4513808739088563","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@emin-bit/pnp-mcp","version":"1.2.0","description":"MCP (Model Context Protocol) server bridging Claude with the PnP PowerShell module for SharePoint Online and Microsoft 365 administration. Wraps `pwsh` 7+ with PnP.PowerShell module installed; exposes 91 typed tools (auth lifecycle, sites/webs/lists/files","type":"module","license":"MIT","author":{"name":"Emin Mujabašić"},"homepage":"https://github.com/Emin-bit/pnp-mcp#readme","repository":{"type":"git","url":"git+https://github.com/Emin-bit/pnp-mcp.git"},"bugs":{"url":"https://github.com/Emin-bit/pnp-mcp/issues"},"keywords":["mcp","model-context-protocol","claude","claude-desktop","anthropic","pnp","pnp-powershell","sharepoint","sharepoint-online","microsoft-365","m365","powershell","pwsh","office-365","tenant-admin","provisioning"],"bin":{"pnp-mcp":"dist/index.js"},"main":"dist/index.js","scripts":{"build":"tsc && chmod +x dist/index.js","start":"node dist/index.js","dev":"tsc --watch","clean":"rm -rf dist","test":"npm run build && node smoke-test.mjs","verify-enums":"node verify-enums.mjs","prepublishOnly":"npm run clean && npm run test && npm run verify-enums"},"engines":{"node":">=18"},"dependencies":{"@modelcontextprotocol/sdk":"^1.0.4","semver":"^7.6.3","update-notifier":"^7.3.1","zod":"^3.23.8"},"devDependencies":{"@types/node":"^22.10.0","@types/semver":"^7.5.8","@types/update-notifier":"^6.0.8","typescript":"^5.6.3"},"_id":"@emin-bit/pnp-mcp@1.2.0","gitHead":"0a3778ca9995eea1aa7b6313befe613ae5fb3e35","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-k87d7SlwJx1MMw3lsE6zUmM5oM8W1/Trx1Wl642kUmc4WwDDL+yj380MhPSrhrFEu4gtt0eTJaXoKAKjgVlrIg==","shasum":"40fe1d9de0181155bd924486c01508ef0bc34f54","tarball":"https://registry.npmjs.org/@emin-bit/pnp-mcp/-/pnp-mcp-1.2.0.tgz","fileCount":40,"unpackedSize":321197,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIE7Pz8z5dMWIZBs8ZZ9NucbHsIbHQ1voofH8aeANKbMgAiEAs6PW+m9lcgw5ZOUopENCHQFr68TK53L5ScuyBc4Pi5M="}]},"_npmUser":{"name":"emin-bit","email":"emin.mujabasic@gmail.com"},"directories":{},"maintainers":[{"name":"emin-bit","email":"emin.mujabasic@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pnp-mcp_1.2.0_1779016764811_0.3632938648556243"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-04T10:50:28.510Z","modified":"2026-05-17T11:19:25.075Z","1.0.0":"2026-05-04T10:50:28.742Z","1.0.1":"2026-05-09T18:43:48.000Z","1.1.0":"2026-05-10T18:46:48.562Z","1.1.1":"2026-05-11T07:22:13.235Z","1.2.0":"2026-05-17T11:19:24.980Z"},"bugs":{"url":"https://github.com/Emin-bit/pnp-mcp/issues"},"author":{"name":"Emin Mujabašić"},"license":"MIT","homepage":"https://github.com/Emin-bit/pnp-mcp#readme","keywords":["mcp","model-context-protocol","claude","claude-desktop","anthropic","pnp","pnp-powershell","sharepoint","sharepoint-online","microsoft-365","m365","powershell","pwsh","office-365","tenant-admin","provisioning"],"repository":{"type":"git","url":"git+https://github.com/Emin-bit/pnp-mcp.git"},"description":"MCP (Model Context Protocol) server bridging Claude with the PnP PowerShell module for SharePoint Online and Microsoft 365 administration. Wraps `pwsh` 7+ with PnP.PowerShell module installed; exposes 91 typed tools (auth lifecycle, sites/webs/lists/files","maintainers":[{"name":"emin-bit","email":"emin.mujabasic@gmail.com"}],"readme":"# @emin-bit/pnp-mcp\n\n[![npm](https://img.shields.io/npm/v/@emin-bit/pnp-mcp.svg)](https://www.npmjs.com/package/@emin-bit/pnp-mcp)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Node](https://img.shields.io/badge/node-%E2%89%A518-brightgreen)](https://nodejs.org/)\n\n**Model Context Protocol server bridging Claude with the [PnP PowerShell](https://pnp.github.io/powershell/) module for SharePoint Online and Microsoft 365 administration.**\n\nWraps a long-lived `pwsh` 7+ REPL session that has the `PnP.PowerShell` module loaded, then exposes auth lifecycle, sites, webs, lists, items, files, content types, fields, permissions, provisioning templates, modern pages, hub sites, M365 groups, and navigation as **90 typed MCP tools** — plus a generic `pnp_run` passthrough for anything else.\n\nWorks on macOS, Linux, and Windows. Cross-platform pwsh discovery, auth state preserved across calls, safe-mode gating for destructive operations, background jobs for long-running provisioning.\n\n---\n\n## Why this exists\n\nPnP PowerShell is the most complete tooling for SharePoint Online + M365 admin work, but it has ~700 cmdlets. Asking an LLM to drive raw PowerShell over a generic shell tool means: cold-start cost on every call, no auth state preservation, no safety net for `Remove-PnPSite`, no schema for the LLM to reason about parameters.\n\nThis server fixes all of that:\n\n- **One pwsh REPL, kept alive for the session.** Auth lives in the process — `Connect-PnPOnline` once, run dozens of cmdlets without re-authenticating.\n- **Typed Zod schemas** for the 89 most-used operations, so Claude picks parameters correctly without consulting cmdlet help.\n- **Safe-mode gating.** Destructive operations (`Remove-*`, `Clear-*`, `New-PnPSite`, `Invoke-PnPTenantTemplate`, etc.) require an explicit `confirm: true`. The check walks every cmdlet in pipelines and multi-statement commands.\n- **Background jobs.** Provisioning a TeamSite or applying a PnP template can take minutes — Claude Desktop's MCP transport has a ~60s timeout. Background jobs run in separate pwsh processes; track via `job_status` / `job_wait`.\n- **Live-enum verification in CI.** Every hardcoded enum string in TypeScript is checked against the live PnP module on each release via `npm run verify-enums`.\n\n---\n\n## Requirements\n\n- **Node.js ≥ 18** (the MCP server)\n- **PowerShell 7+** (`pwsh` on PATH) — _Windows PowerShell 5.1 is **not** supported_\n- **PnP.PowerShell module** — install via the bundled `setup_install_pnp_module` tool, or manually:\n  ```powershell\n  Install-Module PnP.PowerShell -Scope CurrentUser\n  ```\n\nThe `preflight` MCP tool diagnoses all of the above and reports what's missing.\n\n---\n\n## Installation\n\n### Global (recommended for Claude Desktop)\n\n```bash\nnpm install -g @emin-bit/pnp-mcp\n```\n\nThis installs a `pnp-mcp` binary on PATH.\n\n### Per-project\n\n```bash\nnpm install @emin-bit/pnp-mcp\n```\n\n---\n\n## Claude Desktop configuration\n\nEdit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\\Claude\\claude_desktop_config.json` (Windows) and add:\n\n```json\n{\n  \"mcpServers\": {\n    \"pnp\": {\n      \"command\": \"pnp-mcp\"\n    }\n  }\n}\n```\n\nOr with `npx` (no global install needed):\n\n```json\n{\n  \"mcpServers\": {\n    \"pnp\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@emin-bit/pnp-mcp\"]\n    }\n  }\n}\n```\n\nRestart Claude Desktop, and the 90 PnP tools become available.\n\n### Auto-update notifications\n\nThe server checks the npm registry at most once per 24 hours (background, cached). When a newer version is found, the next launch prints a plain-text banner to stderr with the upgrade command. Set `\"env\": { \"PNP_MCP_DISABLE_UPDATE_CHECK\": \"1\" }` in the MCP entry to silence.\n\nFor users who'd rather have updates pulled automatically (with a 1–3 second cold-start cost), use the `npx ... @latest` form:\n\n```json\n{\n  \"mcpServers\": {\n    \"pnp\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@emin-bit/pnp-mcp@latest\"]\n    }\n  }\n}\n```\n\n`npx` will check for and install a newer version on every Claude Desktop launch. Note: a server restart is still required to load the new code, but Claude Desktop already restarts MCP servers on each app start.\n\n### Disabling safe-mode (NOT recommended)\n\nDestructive cmdlets are gated behind `confirm: true` by default. If you really want to skip the gate (e.g. for fully automated pipelines), set:\n\n```json\n{\n  \"mcpServers\": {\n    \"pnp\": {\n      \"command\": \"pnp-mcp\",\n      \"env\": { \"PNP_MCP_SAFE_MODE\": \"off\" }\n    }\n  }\n}\n```\n\nTreat this like `rm -rf /` — there is no undo for `Remove-PnPSite -Force`.\n\n---\n\n## Quickstart\n\nAfter Claude Desktop reconnects, ask Claude something like:\n\n> _Run preflight to check my PnP setup._\n\nClaude calls `preflight` → reports Node, pwsh, module, auth status. If the module is missing:\n\n> _Install the PnP module._\n\nClaude calls `setup_install_pnp_module` (with `confirm: true`). Then connect:\n\n> _Connect interactively to https://contoso.sharepoint.com/sites/marketing._\n\nClaude picks `pnp_auth_connect_interactive` (browser pop-up). Then:\n\n> _What lists are on this site?_\n\nClaude calls `pnp_list_list`.\n\n> _Create a list called \"Project Tracker\" with a Title and a DueDate field._\n\nClaude calls `pnp_list_new` (with `confirm: true`), then `pnp_field_add` for the date column.\n\nFor more end-to-end flows, see [`examples/`](./examples).\n\n---\n\n## Tool catalog\n\n90 tools across 5 phases. The server's MCP `instructions` text (sent at handshake) is the canonical guide for the LLM; this list is for humans browsing the README.\n\n<details>\n<summary><strong>Phase 1 — auth, preflight, jobs</strong> (12 tools)</summary>\n\n- `preflight` — diagnose Node, pwsh, PnP module, auth state\n- `setup_install_pnp_module` — install PnP.PowerShell via `Install-Module`\n- `pnp_auth_connect_interactive` — browser pop-up\n- `pnp_auth_connect_device_code` — URL + code flow (headless)\n- `pnp_auth_connect_sp_secret` — service principal + secret\n- `pnp_auth_connect_sp_cert` — service principal + .pfx certificate\n- `pnp_auth_connect_managed_identity` — Azure resource MI (with IMDS pre-check)\n- `pnp_auth_disconnect` — clear the session's PnP connection\n- `pnp_session_status` — show current connection (URL, account, scopes)\n- `job_list`, `job_status`, `job_wait`, `job_cancel` — manage background jobs\n\n</details>\n\n<details>\n<summary><strong>Phase 2 — sites, webs, tenant</strong> (10 tools)</summary>\n\n- `pnp_site_list`, `pnp_site_get`, `pnp_site_get_by_url`\n- `pnp_site_new` (TeamSite / CommunicationSite / TeamSiteWithoutMicrosoft365Group; defaults `background: true`)\n- `pnp_site_remove` (defaults `background: true`)\n- `pnp_web_list`, `pnp_web_get`, `pnp_web_new`, `pnp_web_remove`\n- `pnp_tenant_get`, `pnp_tenant_set`\n\n</details>\n\n<details>\n<summary><strong>Phase 3 — lists, items, views, files, folders</strong> (18 tools)</summary>\n\n- `pnp_list_list`, `pnp_list_get`, `pnp_list_new`, `pnp_list_set`, `pnp_list_remove`\n- `pnp_listitem_list`, `pnp_listitem_get`, `pnp_listitem_add`, `pnp_listitem_set`, `pnp_listitem_remove`\n- `pnp_view_list`, `pnp_view_add`, `pnp_view_remove`\n- `pnp_file_get`, `pnp_file_add`, `pnp_file_copy`, `pnp_file_move`, `pnp_file_remove`\n- `pnp_folder_get`, `pnp_folder_add`, `pnp_folder_remove`\n\n</details>\n\n<details>\n<summary><strong>Phase 4 — schema, permissions, provisioning</strong> (19 tools)</summary>\n\n- `pnp_contenttype_list`, `pnp_contenttype_get`, `pnp_contenttype_add`, `pnp_contenttype_set`, `pnp_contenttype_remove`\n- `pnp_field_list`, `pnp_field_get`, `pnp_field_add`, `pnp_field_set`, `pnp_field_remove` (22-value `FieldType` enum)\n- `pnp_group_list`, `pnp_group_get`, `pnp_group_new`, `pnp_group_set`, `pnp_group_remove`\n- `pnp_role_definition_list`, `pnp_role_set_web`, `pnp_role_set_list`, `pnp_role_set_listitem`\n- `pnp_template_get` (export PnP XML), `pnp_template_apply` (defaults `background: true`)\n\n</details>\n\n<details>\n<summary><strong>Phase 5 — pages, hubs, M365 groups, navigation</strong> (24 tools)</summary>\n\n- `pnp_page_list`, `pnp_page_get`, `pnp_page_add`, `pnp_page_set`, `pnp_page_remove` (LayoutType incl. `Dashboard`/`NewsDigest`; `PromoteAs` uses `NewsArticle`)\n- `pnp_hubsite_list`, `pnp_hubsite_register`, `pnp_hubsite_set`, `pnp_hubsite_associate`, `pnp_hubsite_disassociate`\n- `pnp_m365group_list`, `pnp_m365group_get`, `pnp_m365group_new`, `pnp_m365group_set`, `pnp_m365group_remove`\n- `pnp_m365group_member_add`, `pnp_m365group_member_remove`, `pnp_m365group_owner_add`, `pnp_m365group_owner_remove`\n- `pnp_navigation_list`, `pnp_navigation_add`, `pnp_navigation_remove` (`all: true` requires `location`)\n\n</details>\n\n<details>\n<summary><strong>Escape hatches</strong> (2 tools)</summary>\n\n- `pnp_run` — execute any PowerShell expression in the live session (gated by safe-mode)\n- `pnp_help` — list/describe PnP cmdlets (`Get-Help` / `Get-Command -Module PnP.PowerShell`)\n\n</details>\n\n---\n\n## Safety model\n\nThe `isDestructive()` check (see [`src/safety.ts`](./src/safety.ts)) inspects each command for:\n\n1. **Destructive verbs** — `Remove`, `Clear`, `Reset`, `Disable`, `Stop`, `Disconnect`, `Revoke`, `Deny`, `Block`, `Uninstall`, `Unpublish`.\n2. **Explicit destructive cmdlets** — list of ~56 cmdlets that aren't covered by the verb rule but mutate tenant/site state (`New-PnPSite`, `Invoke-PnPTenantTemplate`, `Set-PnPListItem`, `Add-PnPField`, `Register-PnPHubSite`, `Add-PnPSiteCollectionAdmin`, …).\n3. **Dangerous parameters** — `-Force`, `-Confirm:$false`, `-IgnoreOnPremError`.\n\nThe check walks **every** Verb-Noun token after stripping comments and string literals — so `Get-PnPListItem | Remove-PnPListItem` and `Connect-PnPOnline ...; Remove-PnPSite ...` are both gated, not just the first cmdlet.\n\nWhen safe-mode blocks a command, the tool returns `isError: true` with a `BLOCKED:` message explaining why, so Claude can decide to retry with `confirm: true` after reading `pnp_session_status`.\n\n---\n\n## Development\n\n```bash\ngit clone https://github.com/Emin-bit/pnp-mcp.git\ncd pnp-mcp\nnpm install\nnpm run build         # tsc → dist/\nnpm test              # 72 smoke tests against a live pwsh session\nnpm run verify-enums  # check every TS enum against live PnP enums\n```\n\nThe smoke test is a real MCP-protocol round-trip: it spawns the built server, sends `initialize` + `tools/list` + 70+ `tools/call` requests, and asserts safety gating, error handling, JSON round-trip integrity, and live-enum coverage. It requires `pwsh` and `PnP.PowerShell` to be installed (`preflight` will tell you what's missing).\n\n### Architecture notes\n\n- **`src/pwsh.ts`** — long-lived REPL session manager. Each tool call sends a base64-encoded user command wrapped in a marker-protocol try/catch, then reads stdout until the END marker. ANSI sequences are stripped before parsing.\n- **`src/safety.ts`** — destructive-command detection (string-aware, comment-aware, pipeline-aware).\n- **`src/runner.ts`** — the common `runAsTool({ toolName, command, timeoutMs })` wrapper used by every typed tool.\n- **`src/jobs.ts`** — background job tracking (separate pwsh per job, no auth inheritance).\n- **`src/tools/*.ts`** — one file per tool family (auth, site, web, list, listitem, view, file, folder, contenttype, field, permission, template, page, hubsite, m365group, navigation, preflight, jobs).\n- **`verify-enums.mjs`** — CI script. Author convention: prefix every `z.enum([…])` with `// @verify-enum [DotNetTypeName]` and the script will assert each TS literal exists in the live .NET enum.\n\n---\n\n## Companion project\n\nThis is a sibling to [`@emin-bit/power-platform-mcp`](https://www.npmjs.com/package/@emin-bit/power-platform-mcp), a separately-installable MCP server for the Power Platform CLI (`pac` / `pacx`). They're independent — install whichever (or both) you need.\n\n---\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n\n## Contributing\n\nIssues and PRs welcome at [github.com/Emin-bit/pnp-mcp](https://github.com/Emin-bit/pnp-mcp). For new tools, please:\n\n1. Add a Zod schema with descriptive `.describe()` text on every parameter.\n2. Add a `// @verify-enum [DotNetType]` marker if you're adding a new enum.\n3. Add a smoke test in `smoke-test.mjs` (at minimum: confirm gating works for destructive operations).\n4. Run `npm run verify-enums` and `npm test` — both must pass.\n","readmeFilename":"README.md"}