diff --git a/modules/connect/images/sharepoint-connect-banner.png b/modules/connect/images/sharepoint-connect-banner.png new file mode 100644 index 0000000..f7b5a74 Binary files /dev/null and b/modules/connect/images/sharepoint-connect-banner.png differ diff --git a/modules/connect/images/sharepoint-create-form.png b/modules/connect/images/sharepoint-create-form.png new file mode 100644 index 0000000..3a44b68 Binary files /dev/null and b/modules/connect/images/sharepoint-create-form.png differ diff --git a/modules/connect/images/sharepoint-entra-app-overview.png b/modules/connect/images/sharepoint-entra-app-overview.png new file mode 100644 index 0000000..df58d34 Binary files /dev/null and b/modules/connect/images/sharepoint-entra-app-overview.png differ diff --git a/modules/connect/images/sharepoint-entra-client-secret.png b/modules/connect/images/sharepoint-entra-client-secret.png new file mode 100644 index 0000000..ae285ca Binary files /dev/null and b/modules/connect/images/sharepoint-entra-client-secret.png differ diff --git a/modules/connect/images/sharepoint-entra-permissions.png b/modules/connect/images/sharepoint-entra-permissions.png new file mode 100644 index 0000000..1b57a88 Binary files /dev/null and b/modules/connect/images/sharepoint-entra-permissions.png differ diff --git a/modules/connect/images/sharepoint-entra-register.png b/modules/connect/images/sharepoint-entra-register.png new file mode 100644 index 0000000..a09bbee Binary files /dev/null and b/modules/connect/images/sharepoint-entra-register.png differ diff --git a/modules/connect/images/sharepoint-inspector-list-sites.png b/modules/connect/images/sharepoint-inspector-list-sites.png new file mode 100644 index 0000000..96f3d76 Binary files /dev/null and b/modules/connect/images/sharepoint-inspector-list-sites.png differ diff --git a/modules/connect/images/sharepoint-marketplace-picker.png b/modules/connect/images/sharepoint-marketplace-picker.png new file mode 100644 index 0000000..265eae3 Binary files /dev/null and b/modules/connect/images/sharepoint-marketplace-picker.png differ diff --git a/modules/connect/images/sharepoint-oauth-provider.png b/modules/connect/images/sharepoint-oauth-provider.png new file mode 100644 index 0000000..fb2e038 Binary files /dev/null and b/modules/connect/images/sharepoint-oauth-provider.png differ diff --git a/modules/connect/pages/managed/managed-catalog.adoc b/modules/connect/pages/managed/managed-catalog.adoc index 853bf8a..00bb077 100644 --- a/modules/connect/pages/managed/managed-catalog.adoc +++ b/modules/connect/pages/managed/managed-catalog.adoc @@ -238,7 +238,7 @@ If any of these answers are "no," prefer xref:connect:register-remote.adoc[a sel |xref:connect:managed/salesforce.adoc[Setup guide] |*SharePoint* badge:beta[label=beta] -|Read and write SharePoint sites, document libraries, lists, and files through the Microsoft Graph API. +|Access SharePoint sites, document libraries, lists, and files through the Microsoft Graph API. |xref:connect:managed/sharepoint.adoc[Setup guide] |*Text Chunker* badge:beta[label=beta] diff --git a/modules/connect/pages/managed/sharepoint.adoc b/modules/connect/pages/managed/sharepoint.adoc index c7e0634..dda881c 100644 --- a/modules/connect/pages/managed/sharepoint.adoc +++ b/modules/connect/pages/managed/sharepoint.adoc @@ -1,13 +1,15 @@ = SharePoint Managed MCP Server :page-beta: true -:description: Let agents read and write SharePoint sites, document libraries, lists, and files through the Microsoft Graph API, using each end-user's own Microsoft identity through user-delegated OAuth. +:description: Let agents read SharePoint sites, document libraries, lists, and files, upload files, and search content through the Microsoft Graph API, using each end-user's own Microsoft identity through user-delegated OAuth. :page-topic-type: how-to :personas: agent_builder, platform_engineer -:learning-objective-1: Register a Microsoft Entra app and an OAuth Provider for the SharePoint managed MCP server +:learning-objective-1: Register a Microsoft Entra app and an OAuth provider for the SharePoint managed MCP server :learning-objective-2: Create the SharePoint managed MCP server with the correct Microsoft Graph scopes :learning-objective-3: Authorize a user and run the SharePoint tools from the Inspector or an agent -The *SharePoint* managed MCP server lets agents read and write SharePoint sites, document libraries, lists, and files through the https://learn.microsoft.com/en-us/graph/api/resources/sharepoint[Microsoft Graph API^]. Each agent caller authenticates against Microsoft with their own identity through user-delegated OAuth, so every call stays bounded by the signed-in user's own SharePoint permissions: the server never holds workspace-wide access. +// Source: `cloudv2` `apps/aigw/internal/mcp/managed/mcps/sharepoint/{register_mcp,register,handler,client}.go`, `proto/mcps/redpanda/mcps/sharepoint/v1/sharepoint_{service,config}.proto`, `proto/mcps/redpanda/mcps/v1/auth.proto`, `apps/aigw/internal/services/oauthprovider/well_known.go`, and `apps/aigw/docs/user-docs/sharepoint-mcp-setup.md` on `origin/main`, verified at 1ce9017a06, 2026-06-11. + +The *SharePoint* managed MCP server lets agents read SharePoint sites, document libraries, lists, and files, upload files, and search content through the https://learn.microsoft.com/en-us/graph/api/resources/sharepoint[Microsoft Graph API^]. Each agent caller authenticates against Microsoft with their own identity through user-delegated OAuth, so every call stays bounded by the signed-in user's own SharePoint permissions: the server never holds workspace-wide access. After reading this page, you will be able to: @@ -17,43 +19,45 @@ After reading this page, you will be able to: [NOTE] ==== -The SharePoint managed MCP server is in beta. Contact Redpanda support if you don't see SharePoint in the picker. +The SharePoint managed MCP server is in beta. If you don't see SharePoint in the picker, contact Redpanda support. ==== == What this MCP server does -The SharePoint managed type wraps the Microsoft Graph API (`https://graph.microsoft.com/v1.0`) and authenticates per-user through the gateway's OAuth token vault. It exposes tools across sites, document libraries, lists, and files: +The SharePoint managed type wraps the Microsoft Graph API (`https://graph.microsoft.com/v1.0`) and authenticates per user through the gateway's glossterm:token vault[]. Per-user OAuth is the only authentication mode this type supports. It exposes the following tools: [cols="1,2"] |=== -|Tool |Description +|Tool |What it does |`list_sites` |Search for SharePoint sites. The search term passes straight to Graph's `/sites?search=` endpoint. An empty search returns no results, so pass `*` to list every site or a name fragment to narrow the search. |`get_site` -|Fetch a single site by ID or hostname path. +|Fetch a single site by composite ID (`contoso.sharepoint.com,,`) or hostname path (`contoso.sharepoint.com:/sites/team`). |`list_drive_items` |Browse a site's default document library. Lists the library root by default; pass `folder_id` to descend into a folder. |`get_file_content` -|Read the content of a file in a site's document library. +|Read a file from a site's document library as UTF-8 text, with its filename, MIME type, and size. |`upload_file` -|Upload a file to a site's document library. Requires the `Sites.ReadWrite.All` scope. +|Upload a text file to a site's document library, into the root or a folder. Requires the `Sites.ReadWrite.All` scope. |`search_content` -|Search content across a site by keyword. +|Search across all SharePoint sites, files, and list items the user can see. The query supports Keyword Query Language (KQL). |`list_lists` -|List the SharePoint lists on a site. +|List the SharePoint lists on a site. Hidden lists are excluded. |`list_list_items` -|List the items in a SharePoint list. +|List the items in a SharePoint list, with each item's column values returned as JSON in `fields_json`. |=== -The file tools operate on the site's own drive (`/sites/{id}/drive/...`), never on a user's personal OneDrive. +The file tools operate on the site's own default document library (`/sites//drive/...`), never on a user's personal OneDrive. File content travels as UTF-8 text in both directions: downloads through `get_file_content` are capped at 10 MiB, and uploads through `upload_file` are capped at 4 MiB (the Microsoft Graph simple-upload limit). Binary files aren't supported; open the `web_url` returned by `list_drive_items` to access them directly. + +The `list_sites`, `list_drive_items`, and `list_list_items` tools return up to 100 items by default and accept a `page_size` of up to 999. The `search_content` tool returns up to 25 hits by default and accepts a `page_size` of up to 500. The `list_lists` tool takes no `page_size`. == Prerequisites @@ -63,32 +67,36 @@ Before you create the server, make sure you have: * Permission to register an app in the https://entra.microsoft.com[Microsoft Entra admin center^] (or an Entra admin who can register one for you). * A way to write the OAuth client secret into the Redpanda ADP secret store. * For most tenants, a tenant admin who can grant admin consent for the Microsoft Graph scopes. See <>. -* Familiarity with xref:connect:user-delegated-oauth.adoc[User-delegated OAuth]. +* Familiarity with xref:connect:user-delegated-oauth.adoc[user-delegated OAuth]. == Register the Microsoft Entra app In the Microsoft Entra admin center, go to *App registrations > New registration* and create an app: -. Give the app a descriptive name, for example `Redpanda AI Gateway - SharePoint MCP`. -. Set `Supported account types` to single tenant. -. Set the `Redirect URI` to platform *Web* with the gateway's OAuth callback for your dataplane: +. Give the app a descriptive name, for example, `Redpanda AI Gateway - SharePoint MCP`. +. Set *Supported account types* to single tenant. +. Set the *Redirect URI* to platform *Web* with the gateway's OAuth callback for your cluster: + [source,no-highlight] ---- -https://aigw..clusters.rdpa.co/oauth/v1/callback +https://aigw..clusters.rdpa.co/oauth/v1/callback ---- + -Replace `` with your dataplane identifier. The callback path is `/oauth/v1/callback`. -. After registering, note the `Application (client) ID` and `Directory (tenant) ID` from the app's Overview page. You need both when you configure the glossterm:OAuth provider[,OAuth Provider]. +Replace `` with your cluster identifier. The callback path is `/oauth/v1/callback`. ++ +image::sharepoint-entra-register.png["The Entra Register an application form with the app name filled in, Supported account types set to single tenant only, and the Redirect URI row set to platform Web with the gateway's OAuth callback URL"] +. After registering, note the *Application (client) ID* and *Directory (tenant) ID* from the app's Overview page. You need both when you configure the glossterm:OAuth provider[]. ++ +image::sharepoint-entra-app-overview.png["The Entra app's Overview page with the Essentials pane showing where the Application (client) ID and Directory (tenant) ID values appear"] [TIP] ==== -Single-tenant apps should use the tenant-specific Microsoft OAuth endpoints (`https://login.microsoftonline.com//oauth2/v2.0/...`), not the `/common/` endpoints, which can return `AADSTS` errors for single-tenant apps. +Single-tenant apps must use the tenant-specific Microsoft OAuth endpoints (`https://login.microsoftonline.com//oauth2/v2.0/...`), not the `/common/` endpoints, which can return `AADSTS` errors for single-tenant apps. Replace `` with the Directory (tenant) ID from the app's Overview page. ==== == Add Microsoft Graph permissions -In the app's *API permissions*, select *Add a permission > Microsoft Graph > Delegated permissions*, then add: +In the Microsoft Entra admin center, open the app's *API permissions*, select *Add a permission > Microsoft Graph > Delegated permissions*, then add: [cols="1,2"] |=== @@ -101,77 +109,97 @@ In the app's *API permissions*, select *Add a permission > Microsoft Graph > Del |Issues refresh tokens so connections stay valid over time. |=== -The `openid`, `profile`, `email`, and `User.Read` scopes come from the OAuth Provider's standard scopes. +The `openid`, `profile`, `email`, and `User.Read` scopes come from the OAuth provider's standard scopes. + +image::sharepoint-entra-permissions.png["The Entra app's API permissions page listing the delegated Microsoft Graph permissions Sites.ReadWrite.All, Sites.Read.All, offline_access, and User.Read, each with admin consent granted"] [IMPORTANT] ==== Use the *Microsoft Graph* permissions, not the legacy *Office 365 SharePoint Online* API in the picker. The server calls Microsoft Graph only. -Use the `Sites.*` scopes, not `Files.*`. Every endpoint the server calls is under `/sites/{id}/...`, which `Sites.ReadWrite.All` authorizes. `Files.ReadWrite.All` would additionally grant access to users' personal OneDrive, which the server never uses. +Use the `Sites.*` scopes, not `Files.*`. The server's file and list endpoints all live under `/sites//...`, and its site lookups and content search are also authorized by the `Sites.*` scopes. The `Files.ReadWrite.All` scope additionally grants access to users' personal OneDrive, which the server never uses. ==== == Create a client secret -In the app's *Certificates & secrets*, select *New client secret*, give it a description, choose an expiry, and select *Add*. Copy the secret value immediately, because Microsoft shows it only once. +In the Microsoft Entra admin center, open the app's *Certificates & secrets* and select *New client secret*. Give it a description, choose an expiry, and select *Add*. Copy the secret value immediately; Microsoft never shows it again. -Store the secret in the ADP secret store under an `UPPER_SNAKE_CASE` key, for example `SHAREPOINT_CLIENT_SECRET`. The OAuth Provider references the secret by name, so the plaintext never enters the MCP server configuration. +image::sharepoint-entra-client-secret.png["The Entra Certificates & secrets page with the Add a client secret panel open, a description entered, and the recommended 180-day expiry selected"] + +Store the secret in the ADP secret store under an `UPPER_SNAKE_CASE` key, for example, `SHAREPOINT_CLIENT_SECRET`. The OAuth provider references the secret by name, so the plaintext never enters the MCP server configuration. [[grant-admin-consent]] == Grant admin consent The SharePoint Graph scopes are high-privilege delegated scopes, so most tenants require a tenant admin to consent before any user can connect. A Global Administrator, Privileged Role Administrator, or Cloud Application Administrator opens the app, selects *API permissions*, then selects *Grant admin consent for ``*. -Admin consent does not escalate access: delegated scopes always stay bounded by the signed-in user's own SharePoint permissions. Admin consent approves the app to request the scopes so that individual users aren't each prompted. Without it, a non-admin user sees a "Need admin approval" message on the consent screen. +Admin consent does not escalate access: delegated scopes always stay bounded by the signed-in user's own SharePoint permissions. Admin consent approves the app to request the scopes so that individual users aren't each prompted. Without it, a non-admin user sees the *Need admin approval* message on the consent screen. + +== Configure the OAuth provider -== Configure the OAuth Provider +Register an OAuth provider in ADP that points at Microsoft's tenant-specific endpoints and references the client secret. See xref:connect:oauth-providers.adoc[Configure an OAuth Provider]. The *Microsoft / Azure AD* preset in the provider catalog pre-fills the endpoints, standard scopes, PKCE, and token auth method; for a single-tenant app, replace its `/common/` endpoints with the tenant-specific ones. Use these values: -Register an OAuth Provider in ADP that points at Microsoft's tenant-specific endpoints and references the client secret. See xref:connect:oauth-providers.adoc[Configure an OAuth Provider]. Use these values: +* *Authorization endpoint*: `https://login.microsoftonline.com//oauth2/v2.0/authorize` +* *Token endpoint*: `https://login.microsoftonline.com//oauth2/v2.0/token` +* *Client ID*: The Application (client) ID from the Entra app. +* *Client secret reference*: The secret-store key, for example, `SHAREPOINT_CLIENT_SECRET`. +* *Token auth method*: Client secret in the POST body, with PKCE enabled (matches the Microsoft preset). +* *Scopes*: `openid`, `email`, `profile`, `User.Read`, `Sites.ReadWrite.All`, `offline_access`. -* `Authorization endpoint`: `https://login.microsoftonline.com//oauth2/v2.0/authorize` -* `Token endpoint`: `https://login.microsoftonline.com//oauth2/v2.0/token` -* `Client ID`: The Application (client) ID from the Entra app. -* `Client secret reference`: The secret-store key, for example `SHAREPOINT_CLIENT_SECRET`. -* `Token auth method`: Client secret in the POST body, with PKCE enabled (matches the Microsoft preset). -* `Scopes`: `openid`, `email`, `profile`, `User.Read`, `Sites.ReadWrite.All`, `offline_access`. +The provider page also shows the *Authorization Callback URL* to register as the Entra app's redirect URI. + +image::sharepoint-oauth-provider.png["The SharePoint OAuth provider detail page showing the authorization callback URL, the tenant-specific Microsoft endpoints, the client ID, the SHAREPOINT_CLIENT_SECRET reference, the Client Secret POST auth method with PKCE required, and the six default scopes"] == Create the server Create a new SharePoint MCP server in ADP: -. Open *MCP Servers > Create Server*. +. Open *MCP Servers > Create server*. . Pick *SharePoint* from the marketplace picker. ++ +image::sharepoint-marketplace-picker.png["The Create MCP Server marketplace picker filtered to SharePoint, showing the SharePoint managed type card next to the Remote (Proxied) option"] . Fill in the identity fields (`name`, `description`). -. Under *Auth*, select *User-delegated OAuth* and pick the SharePoint OAuth Provider you configured. -. Set `Required scopes` to `Sites.ReadWrite.All` (or `Sites.Read.All` for a read-only server). -. Click *Create*. +. Under *Auth*, select *User OAuth* (the only option for this type), then set *Provider Name* to the SharePoint OAuth provider you configured. +. Under *Required Scopes*, add `Sites.ReadWrite.All` (or `Sites.Read.All` for a read-only server). ++ +image::sharepoint-create-form.png["The Configure SharePoint MCP Server form with User OAuth selected, the SharePoint provider chosen, Sites.ReadWrite.All as a required scope, and the request preview panel showing the resulting userOauth JSON payload"] +. Click *Submit*. -The `required_scopes` value is enforced against each user's connection. A connection with insufficient scopes returns `scope_upgrade_required`, and the user re-consents with the higher scope. +The required scopes are enforced against each user's connection. A connection with insufficient scopes returns `scope_upgrade_required`, and the user re-consents with the higher scope. === Configure from the CLI +Use xref:reference:rpk/rpk-ai/rpk-ai.adoc[`rpk ai`] to create the server with a managed config. Set the OAuth provider inside the managed config; the top-level `--user-oauth-provider` and `--user-oauth-scopes` flags apply to self-managed servers only. + [source,bash] ---- rpk ai mcp create --name sharepoint \ --description "SharePoint MCP over Microsoft Graph with per-user OAuth" \ --managed-config '{ "@type": "type.googleapis.com/redpanda.mcps.sharepoint.v1.SharePointMCPConfig", - "user_oauth": { "provider_name": "sharepoint", "required_scopes": ["Sites.ReadWrite.All"] } + "userOauth": { + "providerName": "sharepoint", + "requiredScopes": ["Sites.ReadWrite.All"] + } }' ---- == Authorize a user and test -The SharePoint server uses per-user OAuth, so the first call from a user who hasn't connected yet returns an authorization prompt: +The SharePoint server uses per-user OAuth, so each user connects their Microsoft identity before tools work for them: -. Open the *Inspector* tab. -. Run a tool that requires the user's identity, for example `list_sites` with the argument `{"search": "*"}`. -. The first call returns `OAuthConnectionRequired` with a Microsoft `authorize_url`. The Inspector surfaces it as a consent prompt. -. Open the authorize URL in a browser signed in as the user, approve the Microsoft consent screen, and let Microsoft redirect back to the gateway callback. The token lands in the vault keyed to the user's identity. -. Re-run `list_sites`. It now returns the sites the user can see. +. Open the server in ADP. If you haven't connected to the SharePoint OAuth provider yet, a connection banner with a *Connect* button appears above the server's tabs. Click *Connect*, sign in to Microsoft, and approve the consent screen. The token lands in the vault keyed to your identity. ++ +If you call a tool before connecting, the call returns an `OAuthConnectionRequired` error carrying an `authorize_url` that points at the gateway's `/oauth/v1/authorize` endpoint. Opening that URL in a browser starts the same Microsoft consent flow. ++ +image::sharepoint-connect-banner.png["The SharePoint server page after creation, showing the OAuth connection banner with a Connect button above the Overview, Connection, and Inspector tabs, the MCP server URL, and the Tools section showing a count of eight"] +. Open the server's *Inspector* tab and run `list_sites` with the argument `{"search": "*"}`. It returns the sites you can see. See xref:connect:test-tools.adoc[Test an MCP Server's Tools with the Inspector]. ++ +image::sharepoint-inspector-list-sites.png["The Inspector tab running list_sites with the search argument set to an asterisk, with the response panel showing a JSON sites array of the SharePoint sites the signed-in user can access"] [TIP] ==== -`list_sites` passes the search term straight to Graph's `/sites?search=` endpoint, which returns nothing for an empty search. Pass `*` to list every site, or a name fragment to narrow the search. +The `list_sites` tool passes the search term straight to Graph's `/sites?search=` endpoint, which returns nothing for an empty search. Pass `*` to list every site, or a name fragment to narrow the search. ==== == Troubleshooting @@ -183,20 +211,29 @@ The SharePoint server uses per-user OAuth, so the first call from a user who has |`OAuthConnectionRequired` |First call from a user with no stored token. The user completes Microsoft's OAuth consent flow, the token lands in the vault, and subsequent calls reuse it. See xref:connect:user-delegated-oauth.adoc[User-delegated OAuth]. -|"Need admin approval" on the Microsoft consent screen -|A tenant admin hasn't granted admin consent for the Graph scopes. See xref:connect:managed/sharepoint.adoc#grant-admin-consent[Grant admin consent]. +|*Need admin approval* on the Microsoft consent screen +|A tenant admin hasn't granted admin consent for the Graph scopes. See <>. |`scope_upgrade_required` -|The server's `required_scopes` was extended after users had already consented. Users re-consent with the higher scope. +|The user's connection has fewer scopes than the server requires, either because the server's required scopes grew or because the user originally consented with fewer. The user re-consents with the higher scope. |`AADSTS` errors during authorization -|A single-tenant app is using the `/common/` OAuth endpoints. Switch the OAuth Provider to the tenant-specific endpoints (`https://login.microsoftonline.com//oauth2/v2.0/...`). +|A single-tenant app is using the `/common/` OAuth endpoints. Switch the OAuth provider to the tenant-specific endpoints (`https://login.microsoftonline.com//oauth2/v2.0/...`). + +|`SharePoint API error (status 403, code: accessDenied)` +|The signed-in user lacks permission on that site, file, or list. Graph errors surface only the HTTP status and Graph error code; the upstream error message is withheld. |`list_sites` returns no sites |An empty search returns nothing. Pass `*` to list every site, or a name fragment. -|`upload_file` fails with a permissions error -|`upload_file` requires the `Sites.ReadWrite.All` scope. A read-only server configured with `Sites.Read.All` cannot upload. +|`upload_file` fails +|The `upload_file` tool requires the `Sites.ReadWrite.All` scope; a read-only server configured with `Sites.Read.All` cannot upload. Content is also capped at 4 MiB and must be UTF-8 text. + +|`get_file_content` reports the file exceeds the byte cap +|Downloads are capped at 10 MiB. Open the file's `web_url` directly instead. + +|Token expired and no refresh token available +|The stored token expired and couldn't be refreshed. Confirm `offline_access` is in the OAuth provider's scopes, then have the user reconnect through the same consent flow. |=== == Next steps @@ -204,3 +241,4 @@ The SharePoint server uses per-user OAuth, so the first call from a user who has * xref:connect:oauth-providers.adoc[Configure an OAuth Provider] * xref:connect:user-delegated-oauth.adoc[User-delegated OAuth] * xref:connect:create-server.adoc[Create an MCP Server] +* xref:connect:test-tools.adoc[Test MCP tools]