sharepoint-mcp
by LesterAJohn
README.md
# sharepoint-mcp
Node.js MCP server for Microsoft SharePoint via Microsoft Graph, built from the FortisAI-compatible MCP skeleton. It includes:
- SharePoint site, search, drive, folder, metadata, text-content, and file-management tools
- Vault-backed Microsoft Graph token lookup by user id
- Postgres-backed configuration management
- stdio and HTTP MCP transports
- Security and operational defaults for production-style patterns
## Purpose
This repository is a local starting point for a FortisAI SharePoint MCP that can later sit behind an OpenAPI gateway. It supports both read-oriented SharePoint discovery/retrieval and admin-gated file-management operations. Mutating tools remain protected by `MCP_ADMIN_AUTH_KEY` when configured.
## SharePoint Architecture
Runtime flow:
1. [src/index.js](src/index.js) boots the app, creates services, and connects MCP stdio transport.
2. [src/config/env.js](src/config/env.js) loads and validates environment configuration.
3. [src/services/configStore.js](src/services/configStore.js) handles Postgres persistence.
4. [src/services/vault.js](src/services/vault.js) handles Vault operations and write retry queue.
5. [src/services/security.js](src/services/security.js) redacts sensitive fields.
6. [src/mcp/server.js](src/mcp/server.js) registers MCP tools and applies auth/error wrappers.
7. [src/http/server.js](src/http/server.js) exposes MCP over HTTP with auth, limits, and access logs.
8. [src/http/index.js](src/http/index.js) boots the dedicated HTTP MCP process.
9. [src/start-both.js](src/start-both.js) starts stdio and HTTP as separate child processes.
Local infrastructure:
- [docker-compose.yml](docker-compose.yml) runs Postgres and Vault for local development.
- [docker-compose.external.yml](docker-compose.external.yml) runs only the MCP app against external Postgres and Vault services.
- [initdb/001_config.sh](initdb/001_config.sh) creates and seeds the app-prefixed config table.
## Registering The MCP Server
This repository can be registered with MCP clients in either stdio mode or HTTP mode.
For local development, stdio is the simplest option:
```json
{
"mcpServers": {
"sharepoint-mcp": {
"command": "npm",
"args": ["run", "start:stdio"],
"cwd": "/Users/lesterjohn/Documents/Github/sharepoint-mcp"
}
}
}
```
For HTTP-capable clients, use `npm run start:http` and point the client at the `/mcp` endpoint.
### Codex
Use stdio for Codex unless you specifically need HTTP. Add the server to your Codex MCP configuration using the local workspace path:
- Config file: `~/.codex/config.toml`
- Transport: stdio
```json
{
"mcpServers": {
"sharepoint-mcp": {
"command": "npm",
"args": ["run", "start:stdio"],
"cwd": "/Users/lesterjohn/Documents/Github/sharepoint-mcp"
}
}
}
```
If you prefer HTTP, run `npm run start:http` in this repository and configure Codex to send MCP requests to `http://127.0.0.1:3000/mcp`.
### VS Code
Use stdio in VS Code for local workspace access, or HTTP if your setup routes MCP servers over a local endpoint:
- Config file: `.vscode/mcp.json`
- Transport: stdio or HTTP
```json
{
"command": "npm",
"args": ["run", "start:stdio"],
"cwd": "/Users/lesterjohn/Documents/Github/sharepoint-mcp"
}
```
If your VS Code setup uses HTTP transport, point it at `http://127.0.0.1:3000/mcp` after starting `npm run start:http`.
### Claude
For Claude Desktop, use stdio and add an MCP server entry that launches the process from this repository:
- Config file: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Transport: stdio
```json
{
"mcpServers": {
"sharepoint-mcp": {
"command": "npm",
"args": ["run", "start:stdio"],
"cwd": "/Users/lesterjohn/Documents/Github/sharepoint-mcp"
}
}
}
```
If you are using a Claude setup that supports MCP over HTTP, point it at `http://127.0.0.1:3000/mcp`.
## Tool Catalog
### Shared Contract (All Tools)
- Transport: MCP tools are registered in `src/mcp/server.js` and return MCP text content containing JSON.
- Response shape:
- Success: `{ ok: true, status: 200, data: <payload> }`
- Handled not found paths: `{ ok: false, status: 404, error: <message> }`
- Errors: `{ ok: false, status: 401|500, error: <message> }` with MCP `isError: true`
- Redaction: output is redacted unless `MCP_ALLOW_SENSITIVE_OUTPUT=true`.
- Authorization: when `MCP_ADMIN_AUTH_KEY` is set, admin/mutating tools require `authorizationKey` that exactly matches it.
- User scope fallback: when `userId` is omitted for config/token-indexed operations, default user scope is used (`MCP_CONFIG_DEFAULT_USER_ID`, default `default`).
### Tool Definitions
#### `query_suggestion_schema_discovery`
- Classification: read-only, low-risk.
- Use when: you need a complete schema and usage playbook for every tool exposed by this MCP server.
- Do not use when: you already know the exact tool to call and only need a direct invocation.
- Parameters:
- `objective?`: optional natural-language goal used to prioritize a recommended tool sequence.
- `tools?`: optional list of tool names to scope discovery.
- `includeSchemas?`: optional boolean, defaults to `true`.
- `includeExamples?`: optional boolean, defaults to `true`.
- Response data includes:
- `summary.totalTools` and category counts.
- `objectiveRecommendations`: best-next tools for the provided objective.
- `tools[]`: per-tool description, risk category, when-to-use guidance, follow-up tools, and input schema summary.
- Example: `{ "objective": "discover SharePoint files", "includeSchemas": true }`
#### `sharepoint_connection_info`
- Classification: read-only, low-risk.
- Use when: validating Graph/Vault token-source wiring.
- Do not use when: you need live SharePoint content.
- Prerequisites: SharePoint env parsed at startup.
- Parameters: none.
- Environment behavior: reports configured token-source strategy and limits; never returns access token values.
- Common failures: service startup/config errors.
- Recommended tools: run before `sharepoint_list_sites` during troubleshooting.
- Example: `{}`
#### `sharepoint_list_sites`
- Classification: read-only, low-risk.
- Use when: discovering sites by text query.
- Do not use when: you already have exact `siteId` (use `sharepoint_get_site`).
- Prerequisites: valid Graph access token via direct arg, env, or Vault.
- Parameters:
- `search?`: non-empty string.
- `limit?`: integer `1..100`.
- `userId?`: non-empty string.
- `accessToken?`: non-empty string (direct override).
- Environment behavior: token resolution order is `accessToken` -> `SHAREPOINT_ACCESS_TOKEN` -> Vault path template.
- Common failures: missing/expired token, Graph 401/403, invalid limit.
- Follow-up tools: `sharepoint_list_drives`, `sharepoint_search`.
- Example: `{ "search": "finance", "limit": 10, "userId": "default" }`
#### `sharepoint_get_site`
- Classification: read-only, low-risk.
- Use when: resolving site metadata by known Graph `siteId`.
- Do not use when: site discovery is needed (use `sharepoint_list_sites`).
- Prerequisites: valid Graph token.
- Parameters: `siteId` (required, non-empty), optional `userId`, optional `accessToken`.
- Common failures: invalid `siteId`, unauthorized token, site not found.
- Follow-up tools: `sharepoint_list_drives`.
- Example: `{ "siteId": "contoso.sharepoint.com,abc,def" }`
#### `sharepoint_search`
- Classification: read-only, low-risk.
- Use when: searching entities (`site`, `drive`, `driveItem`, `list`, `listItem`) from one query.
- Do not use when: listing a known folder path (use `sharepoint_list_folder`).
- Prerequisites: valid Graph token and tenant search permissions.
- Parameters:
- `query`: required non-empty string.
- `entityTypes?`: array of enum values above.
- `limit?`: integer `1..100`.
- `userId?`, `accessToken?`.
- Common failures: query validation, Graph permission scopes, throttling.
- Follow-up tools: `sharepoint_get_file_metadata`, `sharepoint_get_file_text`.
- Example: `{ "query": "Q4 roadmap", "entityTypes": ["driveItem"], "limit": 25 }`
#### `sharepoint_list_drives`
- Classification: read-only, low-risk.
- Use when: listing document libraries for a known site.
- Do not use when: site ID is unknown.
- Prerequisites: valid `siteId` and Graph token.
- Parameters: `siteId` (required), `limit?` (`1..100`), `userId?`, `accessToken?`.
- Common failures: invalid site identifier, unauthorized token.
- Follow-up tools: `sharepoint_list_folder`.
- Example: `{ "siteId": "contoso.sharepoint.com,abc,def", "limit": 20 }`
#### `sharepoint_list_folder`
- Classification: read-only, low-risk.
- Use when: enumerating children in a document library folder.
- Do not use when: downloading file body text (use `sharepoint_get_file_text`).
- Prerequisites: valid Graph token and addressable location.
- Parameters:
- `siteId?`, `driveId?`, `itemId?`, `path?` (location selectors).
- `limit?`: integer `1..100`.
- `userId?`, `accessToken?`.
- Parameter constraints: pass a resolvable location set; typically `siteId` with one of `itemId` or `path`.
- Common failures: ambiguous or invalid location parameters, permission denials.
- Follow-up tools: `sharepoint_get_file_metadata`, `sharepoint_get_file_text`.
- Example: `{ "siteId": "contoso.sharepoint.com,abc,def", "path": "Shared Documents/Plans", "limit": 50 }`
#### `sharepoint_get_file_metadata`
- Classification: read-only, low-risk.
- Use when: reading file metadata without body content.
- Do not use when: you need actual file text.
- Prerequisites: valid Graph token and file locator.
- Parameters: optional `siteId`, `driveId`, `itemId`, `path`, plus optional `userId`/`accessToken`.
- Common failures: unresolved item selector, permission denied.
- Follow-up tools: `sharepoint_get_file_text`.
- Example: `{ "siteId": "contoso.sharepoint.com,abc,def", "itemId": "01ABCDEF..." }`
#### `sharepoint_get_file_text`
- Classification: read-only, medium-risk (content exposure).
- Use when: retrieving UTF-8 text from a known file.
- Do not use when: file is binary/large (prefer metadata or downstream export flow).
- Prerequisites: valid Graph token and readable file locator.
- Parameters: optional `siteId`, `driveId`, `itemId`, `path`; `maxBytes?` integer `1..1000000`; optional `userId`/`accessToken`.
- Environment behavior: output redaction still applies but file payload may contain sensitive business data.
- Common failures: non-text content decoding, size limits, permission errors.
- Safety warning: avoid using broad `maxBytes` in production workflows unless downstream handling is controlled.
- Follow-up tools: none required; optionally `token_rotation_config` for long-running jobs.
- Example: `{ "siteId": "contoso.sharepoint.com,abc,def", "path": "Shared Documents/notes.txt", "maxBytes": 200000 }`
#### `sharepoint_create_folder`
- Classification: mutating, high-risk.
- Use when: creating a folder in a known drive/location.
- Do not use when: you only need to inspect existing folders.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Parameters:
- `name`: required non-empty string.
- `siteId?`, `driveId?`, `parentItemId?`, `parentPath?`.
- `conflictBehavior?`: `rename|fail|replace`.
- `userId?`, `accessToken?`, `authorizationKey?`.
- Common failures: unresolved parent selector, unauthorized token, conflict behavior `fail` collisions.
- Example: `{ "siteId": "contoso.sharepoint.com,abc,def", "parentPath": "Shared Documents", "name": "Q4", "conflictBehavior": "rename", "authorizationKey": "<admin-key>" }`
#### `sharepoint_upload_file`
- Classification: mutating, high-risk.
- Use when: uploading a new file under a known parent location.
- Do not use when: modifying content for an existing item id/path (use `sharepoint_update_file`).
- Permissions: `authorizationKey` required when admin auth is enabled.
- Parameters:
- `fileName`: required non-empty string.
- One of `contentText` or `contentBase64` is required.
- `siteId?`, `driveId?`, `parentItemId?`, `parentPath?`, `userId?`, `accessToken?`, `authorizationKey?`.
- Common failures: missing content input, invalid base64 payloads, write permission denial.
- Example: `{ "siteId": "contoso.sharepoint.com,abc,def", "parentPath": "Shared Documents/Q4", "fileName": "notes.txt", "contentText": "hello", "authorizationKey": "<admin-key>" }`
#### `sharepoint_update_file`
- Classification: mutating, high-risk.
- Use when: replacing file content for a known item id/path.
- Do not use when: only metadata changes are required.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Parameters:
- One of `itemId` or `path` should be provided.
- One of `contentText` or `contentBase64` is required.
- `siteId?`, `driveId?`, `userId?`, `accessToken?`, `authorizationKey?`.
- Common failures: unresolved target selector, missing content input, permission denial.
- Example: `{ "siteId": "contoso.sharepoint.com,abc,def", "itemId": "01ABCDEF...", "contentText": "updated", "authorizationKey": "<admin-key>" }`
#### `sharepoint_move_item`
- Classification: mutating, high-risk.
- Use when: renaming and/or moving an item to a different parent.
- Do not use when: you need a duplicate instead of relocation (use `sharepoint_copy_item`).
- Permissions: `authorizationKey` required when admin auth is enabled.
- Parameters:
- `itemId`: required non-empty string.
- At least one of `newName` or `newParentItemId` is required.
- `siteId?`, `driveId?`, `userId?`, `accessToken?`, `authorizationKey?`.
- Common failures: invalid target parent id, permission denial, missing move intent fields.
- Example: `{ "siteId": "contoso.sharepoint.com,abc,def", "itemId": "01ABCDEF...", "newName": "notes-v2.txt", "authorizationKey": "<admin-key>" }`
#### `sharepoint_copy_item`
- Classification: mutating, high-risk.
- Use when: copying a file/folder, optionally to a different parent/drive.
- Do not use when: you need in-place mutation.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Parameters:
- `itemId`: required non-empty string.
- Optional `newName`, `targetDriveId`, `targetParentItemId`.
- `siteId?`, `driveId?`, `userId?`, `accessToken?`, `authorizationKey?`.
- Response behavior: Graph may return an async operation location header; response includes `location` when provided.
- Example: `{ "siteId": "contoso.sharepoint.com,abc,def", "itemId": "01ABCDEF...", "newName": "notes-copy.txt", "authorizationKey": "<admin-key>" }`
#### `sharepoint_delete_item`
- Classification: mutating, high-risk.
- Use when: deleting a known item by id or path.
- Do not use when: soft-retention/audit policy requires archival first.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Parameters:
- One of `itemId` or `path` should be provided.
- `siteId?`, `driveId?`, `userId?`, `accessToken?`, `authorizationKey?`.
- Common failures: unresolved selector, permission denial, retention/lock constraints.
- Example: `{ "siteId": "contoso.sharepoint.com,abc,def", "itemId": "01ABCDEF...", "authorizationKey": "<admin-key>" }`
#### `connection_info`
- Classification: read-only, low-risk.
- Use when: inspecting effective server/Vault/Postgres connectivity configuration.
- Do not use when: validating liveness (use `healthcheck`).
- Prerequisites: service is running.
- Parameters: none.
- Response data includes: server metadata, Vault connection info, Postgres host/port/db plus user/password presence markers.
- Common failures: startup misconfiguration.
- Example: `{}`
#### `vault_connection_info`
- Classification: read-only, low-risk.
- Use when: checking active Vault mode/address/namespace style details.
- Do not use when: validating actual secret read/write permission.
- Prerequisites: Vault client initialized.
- Parameters: none.
- Follow-up tools: `healthcheck`, `get_secret`.
- Example: `{}`
#### `healthcheck`
- Classification: read-only, low-risk.
- Use when: testing Postgres and Vault connectivity in one call.
- Do not use when: you need detailed service diagnostics.
- Prerequisites: Postgres and Vault reachable.
- Parameters: none.
- Expected data: `{ postgres: "ok", vault: "ok" }`.
- Common failures: network, auth, or unavailable dependency.
- Example: `{}`
#### `list_configs`
- Classification: read-only, low-risk.
- Use when: auditing config keys by optional prefix and user scope.
- Do not use when: retrieving one exact key (use `get_config`).
- Prerequisites: Postgres available.
- Parameters: `prefix?` non-empty string, `userId?` non-empty string.
- Environment behavior: defaults to configured default user scope when omitted.
- Common failures: Postgres connectivity issues.
- Follow-up tools: `get_config`, `set_config`.
- Example: `{ "prefix": "vault.agent.", "userId": "default" }`
#### `get_config`
- Classification: read-only, low-risk.
- Use when: reading one config value.
- Do not use when: you need all configs by namespace.
- Prerequisites: Postgres available.
- Parameters: `key` required non-empty string, `userId?`.
- Common failures: returns `{ ok:false, status:404 }` when key missing in scope.
- Follow-up tools: `set_config` if key must be created/updated.
- Example: `{ "key": "token.rotation.intervalMs", "userId": "default" }`
#### `set_config`
- Classification: mutating, medium-risk.
- Use when: creating/updating non-secret configuration.
- Do not use when: storing credentials/secrets (use Vault tools).
- Permissions: `authorizationKey` required when `MCP_ADMIN_AUTH_KEY` is configured.
- Prerequisites: Postgres write access.
- Parameters: `key` required, `value` required (`unknown` JSON type), optional `userId`, optional `authorizationKey`.
- Common failures: unauthorized key, Postgres write failures, invalid key formatting in callers.
- Safety warning: config changes can alter runtime behavior immediately for subsequent calls.
- Follow-up tools: `get_config`, `list_configs`.
- Example: `{ "key": "vault.agent.auth.mode", "value": "listener", "authorizationKey": "<admin-key>" }`
#### `delete_config`
- Classification: mutating, high-risk.
- Use when: intentionally removing config overrides.
- Do not use when: you only want temporary behavior changes.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Prerequisites: Postgres write access.
- Parameters: `key` required, optional `userId`, optional `authorizationKey`.
- Common failures: unauthorized, dependency/database errors.
- Safety warning: deletion can trigger fallback defaults and unexpected behavior shifts.
- Follow-up tools: `get_config`, `token_rotation_config`.
- Example: `{ "key": "vault.agent.listener.addr", "userId": "default", "authorizationKey": "<admin-key>" }`
#### `list_secrets`
- Classification: read-only, medium-risk (metadata disclosure).
- Use when: discovering child keys under a Vault prefix.
- Do not use when: strict least-knowledge policy disallows key name visibility.
- Prerequisites: Vault list permission for prefix.
- Parameters: `prefix?` string.
- Expected data: `{ keys: string[] }`.
- Common failures: Vault ACL denies list, path mount mismatch.
- Follow-up tools: `get_secret`.
- Example: `{ "prefix": "sharepoint/oauth/default" }`
#### `get_secret`
- Classification: read-only, high-risk (secret material).
- Use when: controlled operational workflows need a Vault secret payload.
- Do not use when: only existence/listing is required.
- Prerequisites: Vault read permission on target path.
- Parameters: `path` required non-empty string.
- Common failures: returns `{ ok:false, status:404 }` when missing; Vault ACL denial.
- Safety warning: treat output as sensitive even when redacted mode is active.
- Follow-up tools: `set_secret`, `delete_secret`.
- Example: `{ "path": "sharepoint/oauth/default/access-token" }`
#### `set_secret`
- Classification: mutating, high-risk.
- Use when: writing structured secret values to Vault KV v2.
- Do not use when: persisting non-secret configuration.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Prerequisites: Vault write permission.
- Parameters: `path` required; `value` required object (`record<string, unknown>`); optional `authorizationKey`.
- Common failures: unauthorized, invalid value shape for caller expectations, Vault write errors.
- Safety warning: overwrites stored secret content at target path.
- Follow-up tools: `get_secret`.
- Example: `{ "path": "service/api/key", "value": { "token": "***" }, "authorizationKey": "<admin-key>" }`
#### `delete_secret`
- Classification: mutating, high-risk/destructive.
- Use when: rotating or decommissioning secret entries.
- Do not use when: you need rollback-safe updates (prefer versioned writes).
- Permissions: `authorizationKey` required when admin auth is enabled.
- Prerequisites: Vault delete permission.
- Parameters: `path` required non-empty string, optional `authorizationKey`.
- Common failures: unauthorized, Vault ACL denial, mount/path mismatch.
- Safety warning: destructive operation; verify path and backup policy before use.
- Follow-up tools: `list_secrets`, `get_secret`.
- Example: `{ "path": "service/api/key", "authorizationKey": "<admin-key>" }`
#### `vault_agent_token_read`
- Classification: read-only, high-risk (token sink material).
- Use when: application workflows must inspect Vault Agent sink token data.
- Do not use when: ordinary health/config checks are sufficient.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Prerequisites: Vault Agent sink path configured and readable.
- Parameters: optional `authorizationKey`.
- Common failures: unauthorized, missing token file, listener/file mode mismatch.
- Follow-up tools: `token_lookup_self`.
- Example: `{ "authorizationKey": "<admin-key>" }`
#### `token_lookup_self`
- Classification: read-only, medium-risk.
- Use when: checking capabilities/TTL of the active Vault token.
- Do not use when: renewing or creating tokens.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Prerequisites: valid Vault token context.
- Parameters: optional `authorizationKey`.
- Common failures: unauthorized, revoked/expired token context.
- Follow-up tools: `token_renew_self`.
- Example: `{ "authorizationKey": "<admin-key>" }`
#### `token_renew_self`
- Classification: mutating, medium-risk.
- Use when: extending TTL for the current Vault token.
- Do not use when: token is non-renewable or policy prohibits renewals.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Parameters: optional `increment` (string duration), optional `authorizationKey`.
- Common failures: token non-renewable, max TTL reached, unauthorized.
- Follow-up tools: `token_lookup_self`.
- Example: `{ "increment": "1h", "authorizationKey": "<admin-key>" }`
#### `token_create`
- Classification: mutating, high-risk.
- Use when: controlled issuance of scoped Vault child tokens.
- Do not use when: static token sharing would violate least privilege.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Prerequisites: Vault policies and role setup.
- Parameters:
- `role_name?`, `policies?[]`, `ttl?`, `period?`, `renewable?`, `explicit_max_ttl?`, `num_uses?` (int >= 0), `display_name?`, `meta?` (`record<string,string>`), `authorizationKey?`.
- Common failures: policy mismatch, invalid TTL fields, ACL denial.
- Safety warning: overbroad token policies can create privilege escalation risk.
- Follow-up tools: `token_lookup_self`, `token_revoke`.
- Example: `{ "policies": ["default"], "ttl": "30m", "display_name": "mcp-child", "authorizationKey": "<admin-key>" }`
#### `token_revoke`
- Classification: mutating, high-risk/destructive.
- Use when: revoking a known Vault token immediately.
- Do not use when: token identifier is uncertain.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Parameters: `token` required non-empty string; optional `authorizationKey`.
- Common failures: token already revoked/not found, ACL denial.
- Safety warning: destructive and immediate; can break active workloads.
- Follow-up tools: `token_lookup_self` (for current token context), operational health checks.
- Example: `{ "token": "hvs.xxxxx", "authorizationKey": "<admin-key>" }`
#### `token_revoke_self`
- Classification: mutating, high-risk/destructive.
- Use when: intentionally terminating the current service token context.
- Do not use when: service must continue serving requests.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Parameters: optional `authorizationKey`.
- Common failures: unauthorized, token already invalid.
- Safety warning: likely disrupts subsequent Vault operations until a new token is loaded.
- Follow-up tools: `healthcheck`, re-auth/token bootstrap workflow.
- Example: `{ "authorizationKey": "<admin-key>" }`
#### `vault_seed_http_token`
- Classification: mutating, high-risk.
- Use when: provisioning HTTP bearer tokens in Vault token index.
- Do not use when: caller needs to supply an existing OAuth token (use `vault_seed_oauth_token`).
- Permissions: `authorizationKey` required when admin auth is enabled.
- Prerequisites: Vault write permission to token index path.
- Parameters:
- `userId?`, `tokenId?`, `scopes?` (string or string[]), `audience?` (string or string[]), `expiresAt?` (string), `path?` (Vault path override), `authorizationKey?`.
- Environment behavior: index path defaults to configured value or app-derived fallback.
- Response data: includes generated `token` (only for this tool), `tokenHash`, `indexPath`, metadata fields.
- Common failures: unauthorized, path ACL denial, merge/write errors.
- Safety warning: generated token is credential material; handle output securely.
- Follow-up tools: `token_lookup_self`, HTTP auth tests.
- Example: `{ "userId": "default", "scopes": ["mcp:invoke"], "authorizationKey": "<admin-key>" }`
#### `vault_seed_oauth_token`
- Classification: mutating, high-risk.
- Use when: storing externally issued OAuth access tokens in Vault index.
- Do not use when: you need the service to generate a new opaque bearer token.
- Permissions: `authorizationKey` required when admin auth is enabled.
- Prerequisites: Vault write permission and trusted token source.
- Parameters:
- `token` required non-empty string.
- Optional `userId`, `tokenId`, `scopes` (string or string[]), `audience` (string or string[]), `expiresAt`, `path`, `authorizationKey`.
- Response data: returns metadata and token hash, not the plaintext token.
- Common failures: unauthorized, invalid token input, Vault write errors.
- Safety warning: never log raw OAuth token in external systems.
- Follow-up tools: `sharepoint_list_sites`, `sharepoint_connection_info`.
- Example: `{ "token": "eyJ...", "userId": "default", "authorizationKey": "<admin-key>" }`
#### `token_rotation_config`
- Classification: read-only, low-risk.
- Use when: inspecting effective rotation interval resolution.
- Do not use when: changing interval values (use `set_config`).
- Prerequisites: Postgres available for scoped overrides.
- Parameters: optional `userId`.
- Environment behavior: resolution order is user scope -> default scope -> env default.
- Common failures: Postgres access failures.
- Follow-up tools: `get_config`, `set_config`.
- Example: `{ "userId": "default" }`
## SharePoint Authentication
The SharePoint tools resolve a Microsoft Graph access token in this order:
1. `accessToken` argument on the tool call. This is useful for direct testing only.
2. `SHAREPOINT_ACCESS_TOKEN` environment variable.
3. Vault secret at `SHAREPOINT_VAULT_ACCESS_TOKEN_PATH`, defaulting to `sharepoint/oauth/{userId}/access-token`.
Vault token secrets may use any of these field names:
- `access_token`
- `accessToken`
- `token`
For FortisAI, prefer Vault storage and pass a server-controlled `userId` from the OpenAPI gateway or wrapper instead of letting arbitrary callers choose another user's token.
## Security Behavior
- Sensitive fields are redacted unless MCP_ALLOW_SENSITIVE_OUTPUT=true.
- Mutating operations can be access controlled with MCP_ADMIN_AUTH_KEY.
- Vault write operations are serialized through an internal queue and retried with exponential backoff.
## Environment Variables
Core:
- APP_NAME
- MCP_SERVER_NAME
- MCP_SERVER_VERSION
- MCP_ALLOW_SENSITIVE_OUTPUT
- MCP_ADMIN_AUTH_KEY
- MCP_TRANSPORT_MODE (`stdio`, `http`, or `both`)
- MCP_CONFIG_DEFAULT_USER_ID
- MCP_TOKEN_ROTATION_DEFAULT_INTERVAL_MS
- MCP_TOKEN_ROTATION_USER_INTERVAL_CONFIG_KEY
- MCP_VAULT_AGENT_AUTH_MODE_CONFIG_KEY
- MCP_VAULT_AGENT_TOKEN_FILE_PATH_CONFIG_KEY
- MCP_VAULT_AGENT_LISTENER_ADDR_CONFIG_KEY
HTTP transport:
- MCP_HTTP_HOST
- MCP_HTTP_PORT
- MCP_HTTP_PATH
- MCP_HTTP_HEALTH_PATH
- MCP_HTTP_AUTH_MODE (`token`, `oauth2`, `both`)
- MCP_HTTP_TOKEN_SOURCE (`vault`, `env`)
- MCP_HTTP_AUTH_TOKENS (comma-separated bearer tokens)
- MCP_HTTP_TRUST_PROXY
- MCP_HTTP_ALLOWED_ORIGINS (comma-separated)
- MCP_HTTP_ALLOWED_IPS (comma-separated)
- MCP_HTTP_MAX_BODY_BYTES
- MCP_HTTP_RATE_LIMIT_WINDOW_MS
- MCP_HTTP_RATE_LIMIT_MAX_REQUESTS
- MCP_HTTP_VAULT_TOKEN_INDEX_PATH
- MCP_HTTP_VAULT_TOKEN_DEFAULT_USER_ID
- MCP_HTTP_VAULT_TOKEN_REQUIRED_SCOPES
- MCP_HTTP_VAULT_TOKEN_REQUIRED_AUDIENCE
- MCP_HTTP_VAULT_TOKEN_CACHE_TTL_MS
- MCP_HTTP_OAUTH2_INTROSPECTION_URL
- MCP_HTTP_OAUTH2_CLIENT_ID
- MCP_HTTP_OAUTH2_CLIENT_SECRET
- MCP_HTTP_OAUTH2_REQUIRED_SCOPES
- MCP_HTTP_OAUTH2_REQUIRED_AUDIENCE
- MCP_HTTP_OAUTH2_TIMEOUT_MS
- MCP_HTTP_OAUTH2_CACHE_TTL_MS
- MCP_HTTP_TLS_ENABLED
- MCP_HTTP_TLS_CERT_PATH
- MCP_HTTP_TLS_KEY_PATH
Postgres:
- POSTGRES_HOST
- POSTGRES_PORT
- POSTGRES_EXPOSED_PORT (host-exposed Docker port; default `5432`)
- POSTGRES_DB
- POSTGRES_USER
- POSTGRES_PASSWORD
Postgres config model:
- Configuration data is multi-user scoped in `sharepoint_config` using composite key `(user_id, key)`.
- MCP config tools accept optional `userId`; when omitted, `MCP_CONFIG_DEFAULT_USER_ID` is used.
- Seed records include:
- `default/sharepoint.feature`
- `default/app.defaults` for future default parameters.
- `default/token.rotation.intervalMs`
- `default/vault.agent.auth.mode`
- `default/vault.agent.tokenFilePath`
- `default/vault.agent.listener.addr`
SharePoint:
- SHAREPOINT_GRAPH_BASE_URL
- SHAREPOINT_DEFAULT_USER_ID
- SHAREPOINT_ACCESS_TOKEN
- SHAREPOINT_VAULT_ACCESS_TOKEN_PATH
- SHAREPOINT_TIMEOUT_MS
- SHAREPOINT_MAX_RESPONSE_CHARS
Vault:
- VAULT_ADDR
- VAULT_EXPOSED_PORT (host-exposed Docker port; default `8200`)
- VAULT_CLUSTER_EXPOSED_PORT (host-exposed Docker port for cluster listener in production compose; default `8201`)
- VAULT_TOKEN
- VAULT_AGENT_ENABLED
- VAULT_AGENT_AUTH_MODE (`none`, `file`, `listener`, `both`)
- VAULT_AGENT_TOKEN_FILE_PATH
- VAULT_AGENT_LISTENER_ENABLED
- VAULT_AGENT_LISTENER_ADDR
- VAULT_UNSEAL_KEY
- VAULT_KV_MOUNT
- VAULT_WRITE_RETRY_ATTEMPTS
- VAULT_WRITE_RETRY_BASE_DELAY_MS
- VAULT_WRITE_RETRY_MAX_DELAY_MS
Compose-exposed HTTP:
- MCP_HTTP_EXPOSED_PORT (host-exposed Docker port for `mcp-http`; default `3000`)
Naming defaults:
- `APP_NAME` defaults to `sharepoint`.
- The Postgres config table defaults to `${APP_NAME}_config`.
- The Vault token index path defaults to `${APP_NAME}/http/auth/token-index`.
- Set only `APP_NAME` to rename the app-scoped Vault/Postgres schema across local and external stores.
Reference values are in [.env.example](.env.example).
## Quick Start
1. Install dependencies.
2. Copy .env.example to .env.
3. Start local services with docker compose up -d.
4. Resolve the managed unseal key: `npm run vault:unseal-key -- --json`.
5. Initialize and unseal local Vault (first run):
```bash
docker exec -e VAULT_ADDR=http://127.0.0.1:8200 sharepoint-mcp-vault vault operator init -key-shares=1 -key-threshold=1 -format=json
docker exec -e VAULT_ADDR=http://127.0.0.1:8200 sharepoint-mcp-vault vault operator unseal <unseal_key_from_init_or_env>
```
6. Seed a test secret in Vault.
7. Start the MCP server with npm start.
8. Run tests with npm test.
## External Services Mode
Use this mode when Vault and Postgres are already managed outside this repository.
Required environment variables:
- `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`
- `VAULT_ADDR`, `VAULT_TOKEN`
Run the app-only compose stack:
```bash
docker compose -f docker-compose.external.yml up -d
```
Notes:
- The app still uses Vault for secrets and Postgres for config.
- The external stack is the same MCP HTTP container, but it skips local Postgres/Vault containers.
- If you keep Vault sealed, the app will still require whatever unseal process your external Vault uses.
### Test Coverage Notes
Current automation includes listener-related coverage for Vault Agent runtime resolution.
- [tests/vault-agent-runtime.test.js](tests/vault-agent-runtime.test.js) validates:
- listener mode resolution from Postgres defaults
- both mode resolution (listener + file)
- fallback to environment values when database mode is invalid
Transport scripts:
```bash
# stdio only (default)
npm run start:stdio
# HTTP transport only
npm run start:http
# run stdio + HTTP as two processes
npm run start:both
```
## License
This project is licensed under the MIT License. See [LICENSE](LICENSE).
## HTTP MCP Endpoint
Default endpoint values:
- MCP URL: `http://127.0.0.1:3000/mcp`
- Health URL: `http://127.0.0.1:3000/healthz`
HTTP transport security controls:
- Every `/mcp` request requires `Authorization: Bearer <token>`.
- For `MCP_HTTP_AUTH_MODE=token`, set `MCP_HTTP_TOKEN_SOURCE=vault` to validate tokens from Vault.
- For `MCP_HTTP_AUTH_MODE=oauth2`, bearer tokens are validated by OAuth2 introspection.
- For `MCP_HTTP_AUTH_MODE=both`, either token strategy can authorize requests.
- Mutating tools still require `authorizationKey` when `MCP_ADMIN_AUTH_KEY` is set.
- Request limits are enforced with:
- `MCP_HTTP_MAX_BODY_BYTES`
- `MCP_HTTP_RATE_LIMIT_WINDOW_MS`
- `MCP_HTTP_RATE_LIMIT_MAX_REQUESTS`
- Optional network restrictions:
- `MCP_HTTP_ALLOWED_ORIGINS`
- `MCP_HTTP_ALLOWED_IPS`
### Vault Multi-User Token Model
Store HTTP bearer tokens in Vault at `MCP_HTTP_VAULT_TOKEN_INDEX_PATH`.
Default-user fallback behavior:
- `MCP_HTTP_VAULT_TOKEN_DEFAULT_USER_ID` defaults to `default`.
- If no non-default users exist in the token index, the default user is always used as fallback.
Supported index shape:
```json
{
"tokens": {
"<sha256(token)>": {
"userId": "user-123",
"tokenId": "tok-123",
"active": true,
"scopes": ["mcp:invoke", "mcp:read"],
"audience": ["codex", "claude"],
"expiresAt": "2026-12-31T23:59:59Z"
}
}
}
```
Notes:
- Store only token hashes in Vault index data, never plaintext tokens.
- `MCP_HTTP_VAULT_TOKEN_REQUIRED_SCOPES` and `MCP_HTTP_VAULT_TOKEN_REQUIRED_AUDIENCE` enforce policy checks.
- This keeps secrets in Vault under the app-prefixed root while configuration remains in the app-prefixed Postgres table.
### Vault HTTP Token Seeding
Use the helper script to generate an opaque bearer token and store it in the Vault user token structure:
```bash
npm run vault:seed-http-token -- --user-id default --json
```
Useful options:
- `--user-id <id>`: Vault user to seed.
- `--token-id <id>`: Optional token id stored with the entry.
- `--scopes <list>`: Comma or space separated scopes.
- `--audience <list>`: Comma or space separated audience values.
- `--expires-at <value>`: Optional ISO timestamp or unix seconds.
- `--path <vault-path>`: Override the token index path.
The script writes the token record under the app-prefixed user structure and mirrors the token in the top-level token map for compatibility.
If you need to reseed a user, run the script again with the same `--user-id` and a new `--token-id`.
### Vault OAuth Token Seeding
Use the helper script to store a provided OAuth access token in the Vault user token structure:
```bash
npm run vault:seed-oauth-token -- --token "$OAUTH_ACCESS_TOKEN" --user-id default --json
```
Useful options:
- `--token <value>`: OAuth access token to seed.
- `--user-id <id>`: Vault user to seed.
- `--token-id <id>`: Optional token id stored with the entry.
- `--scopes <list>`: Comma or space separated scopes.
- `--audience <list>`: Comma or space separated audience values.
- `--expires-at <value>`: Optional ISO timestamp or unix seconds.
- `--path <vault-path>`: Override the token index path.
The script stores the provided token under the app-prefixed user structure, keeps the top-level token map aligned, and marks the entry as `oauth2` in Vault metadata.
### MCP Tool
The same capability is exposed as an MCP tool for controlled setup workflows:
- `vault_seed_http_token`: generate a bearer token and store it in the Vault HTTP token index for a user.
- `vault_seed_oauth_token`: store a provided OAuth access token in the Vault HTTP token index for a user.
The tool requires `authorizationKey` when `MCP_ADMIN_AUTH_KEY` is configured.
### Vault Token Lifecycle MCP Tools
The server exposes node-vault token lifecycle methods as MCP tools:
- `token_lookup_self` -> `tokenLookupSelf`
- `token_renew_self` -> `tokenRenewSelf`
- `token_create` -> `tokenCreate`
- `token_revoke` -> `tokenRevoke`
- `token_revoke_self` -> `tokenRevokeSelf`
These tools are intended for controlled operational usage and are guarded by admin authorization when `MCP_ADMIN_AUTH_KEY` is configured.
### Vault Agent Auto-Auth and Token Renewal
Vault Agent can own token auth/renewal while this service reads the sink token file.
- Enable with `VAULT_AGENT_ENABLED=true`
- Choose auth mode with `VAULT_AGENT_AUTH_MODE`:
- `file`: use Vault Agent token sink file
- `listener`: use Vault Agent listener endpoint
- `both`: enable listener operations and file-based token read workflows
- Configure sink file path with `VAULT_AGENT_TOKEN_FILE_PATH`
- Enable listener with `VAULT_AGENT_LISTENER_ENABLED=true`
- Configure listener with `VAULT_AGENT_LISTENER_ADDR`
- Use `vault_agent_token_read` when application workflows need token sink visibility
When Vault Agent mode is enabled:
- File mode refreshes token state from the configured token sink path.
- Listener mode routes Vault operations through the configured Vault Agent listener.
- Both mode supports listener operations and token sink read workflows.
### Option 3: Postgres-Backed Non-Secret Vault Agent Settings
This server supports storing non-secret Vault Agent runtime pointers/settings in Postgres while keeping token material in Vault.
- Runtime settings are read from default user config scope (`MCP_CONFIG_DEFAULT_USER_ID`).
- Key names are configurable with:
- `MCP_VAULT_AGENT_AUTH_MODE_CONFIG_KEY`
- `MCP_VAULT_AGENT_TOKEN_FILE_PATH_CONFIG_KEY`
- `MCP_VAULT_AGENT_LISTENER_ADDR_CONFIG_KEY`
- Recommended values in Postgres:
- `vault.agent.auth.mode`
- `vault.agent.tokenFilePath`
- `vault.agent.listener.addr`
### Rotation Time Configuration
Rotation interval supports both global defaults and user-scoped overrides:
- Global default env variable: `MCP_TOKEN_ROTATION_DEFAULT_INTERVAL_MS`
- User-scoped config key name: `MCP_TOKEN_ROTATION_USER_INTERVAL_CONFIG_KEY` (default `token.rotation.intervalMs`)
Effective value resolution is:
1. User-scoped Postgres config (`userId` + key)
2. Default user Postgres config (`default` + key)
3. Global env default
Use `token_rotation_config` tool to inspect the resolved rotation interval for a user scope.
Minimal remote call example:
```bash
curl -i http://127.0.0.1:3000/mcp \
-H "Authorization: Bearer replace-me-token" \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": { "name": "client", "version": "1.0.0" }
}
}'
```
## HTTPS Deployment Choice
This repository uses a reverse proxy (recommended): terminate TLS at a reverse proxy or load balancer.
- Keep this app on internal HTTP.
- Enforce HTTPS, client allowlists, and edge-level controls at the proxy/LB.
- Forward traffic to `MCP_HTTP_HOST:MCP_HTTP_PORT`.
- Keep `MCP_HTTP_TLS_ENABLED=false` in this process mode.
Popular patterns include Nginx, Traefik, Envoy, ALB/NLB, or Cloudflare Tunnel in front of `/mcp`.
## Vault Production Migration (Raft)
The repository now includes a Vault production migration scaffold under [vault-production](vault-production):
- [vault-production/config/vault.hcl](vault-production/config/vault.hcl): Raft-backed Vault server configuration.
- [vault-production/docker-compose.vault-prod.yml](vault-production/docker-compose.vault-prod.yml): Compose definition for Vault in server mode (non-dev).
- [vault-production/scripts/convert-dev-to-prod.sh](vault-production/scripts/convert-dev-to-prod.sh): Script to export dev KV data, start Raft Vault, initialize/unseal, and import secrets.
- [vault-production/scripts/bootstrap-post-conversion.sh](vault-production/scripts/bootstrap-post-conversion.sh): Script to enable audit, write policy, configure AppRole, and emit service credentials.
- [scripts/vault-unseal-key.js](scripts/vault-unseal-key.js): Script to resolve unseal key from `VAULT_UNSEAL_KEY` or `src/config/vault.unseal.key.json`.
- [vault-production/README.md](vault-production/README.md): Detailed migration notes and options.
Managed unseal key flow:
- `VAULT_UNSEAL_KEY` is optional and can be injected at runtime.
- If `VAULT_UNSEAL_KEY` is not set, `npm run vault:unseal-key` reads `src/config/vault.unseal.key.json`.
- If the key file is missing or empty, a 24-character key is generated and saved to `src/config/vault.unseal.key.json`.
- Both compose stacks run a one-shot `vault-unseal-key-init` service before Vault startup to ensure key material exists.
Run the conversion:
```bash
bash vault-production/scripts/convert-dev-to-prod.sh --init-keys-out vault-production/backups/vault-init.json
```
Common options:
```bash
# Use a different KV mount
bash vault-production/scripts/convert-dev-to-prod.sh --mount secret
# Migrate infra only (skip data movement)
bash vault-production/scripts/convert-dev-to-prod.sh --skip-export --skip-import
# Use when Raft Vault is already initialized
bash vault-production/scripts/convert-dev-to-prod.sh --skip-init
# Explicitly set key file path used by convert script
bash vault-production/scripts/convert-dev-to-prod.sh --unseal-key-path src/config/vault.unseal.key.json
# Post-conversion hardening bootstrap (audit, policy, AppRole)
bash vault-production/scripts/bootstrap-post-conversion.sh --vault-token <root_or_admin_token>
# CI-friendly machine output
bash vault-production/scripts/bootstrap-post-conversion.sh \
--vault-token <root_or_admin_token> \
--output json
```
## VS Code Agent Structure
This repository includes a project agent structure for adapting this MCP server to additional service-backed MCP implementations:
- [agent/README.md](agent/README.md): Overview of agent assets.
- [agent/playbooks/service-onboarding.md](agent/playbooks/service-onboarding.md): Step-by-step onboarding checklist.
- [agent/templates/service-spec.md](agent/templates/service-spec.md): Request template for describing new service integrations.
Workspace custom agent:
- [.github/agents/sharepoint-services-mcp.agent.md](.github/agents/sharepoint-services-mcp.agent.md)
- [.github/prompts/adapt-sharepoint-service.prompt.md](.github/prompts/adapt-sharepoint-service.prompt.md)
Use this custom agent when you want GitHub Copilot in VS Code to:
1. Configure new service adapters under `src/services`.
2. Register matching MCP tools in `src/mcp/server.js`.
3. Update env validation in `src/config/env.js`.
4. Preserve authorization and redaction behavior.
5. Add tests and documentation updates.
## Test Coverage
Integration tests in [tests/server.integration.test.js](tests/server.integration.test.js) cover:
- Healthcheck behavior
- Authorization on mutating tools
- Redaction behavior for secret output
HTTP transport tests in [tests/http.integration.test.js](tests/http.integration.test.js) cover:
- Unauthorized requests are rejected
- Authorized MCP initialization succeeds
- Internal failures return JSON-RPC-compatible error responses
- Health endpoint behavior
Vault token auth tests in [tests/vault-token-auth.test.js](tests/vault-token-auth.test.js) cover:
- Multi-user token index lookup by SHA-256 hash
- Inactive token rejection
- Scope/audience-aware authorization inputs
Production migration tests in [tests/vault-production.test.js](tests/vault-production.test.js) cover:
- Presence of Vault production scaffold files
- Raft config expectations
- Non-dev Vault compose command validation
- Conversion/bootstrap script help and bash syntax checks
## Extend The Server
1. Add domain services under src/services.
2. Register new tools in src/mcp/server.js.
3. Add corresponding tests under tests.
4. Keep mutating tools behind authorization checks.
5. Keep secret-bearing fields redacted by default.
## Notes
- docker-compose.yml now runs Vault with Raft-backed storage for local persistence.
- The managed key script is an automation helper and not a Vault KMS/HSM auto-unseal backend.
- The migration scaffold starts with bootstrap-friendly defaults and still requires TLS, production auth methods, and credential rotation before real production use.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues