diff --git a/modules/ROOT/nav.adoc b/modules/ROOT/nav.adoc index 87c011f..c642cde 100644 --- a/modules/ROOT/nav.adoc +++ b/modules/ROOT/nav.adoc @@ -26,6 +26,7 @@ **** xref:connect:managed/netsuite.adoc[NetSuite] **** xref:connect:managed/openapi.adoc[OpenAPI] **** xref:connect:managed/ramp.adoc[Ramp] +**** xref:connect:managed/sharepoint.adoc[SharePoint] **** xref:connect:managed/slack.adoc[Slack] **** xref:connect:managed/sql.adoc[SQL] **** xref:connect:managed/workday.adoc[Workday] diff --git a/modules/connect/pages/managed/managed-catalog.adoc b/modules/connect/pages/managed/managed-catalog.adoc index ceb8152..998b1e6 100644 --- a/modules/connect/pages/managed/managed-catalog.adoc +++ b/modules/connect/pages/managed/managed-catalog.adoc @@ -213,6 +213,10 @@ If any of these answers are "no," prefer xref:connect:register-remote.adoc[a sel |Query, create, update, and delete Salesforce CRM records using SOQL and the REST API, and run and inspect saved Salesforce reports. | +|*SharePoint* +|Read and write SharePoint sites, document libraries, lists, and files through the Microsoft Graph API. +|xref:connect:managed/sharepoint.adoc[See the deep-dive] + |*Text Chunker* |Split and chunk text for RAG and LLM ingestion pipelines. | diff --git a/modules/connect/pages/managed/sharepoint.adoc b/modules/connect/pages/managed/sharepoint.adoc new file mode 100644 index 0000000..5a4e4c4 --- /dev/null +++ b/modules/connect/pages/managed/sharepoint.adoc @@ -0,0 +1,209 @@ += SharePoint Managed MCP Server +: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. +: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-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. + +After reading this page, you will be able to: + +* [ ] {learning-objective-1} +* [ ] {learning-objective-2} +* [ ] {learning-objective-3} + +[NOTE] +==== +The SharePoint managed MCP server is in early access and is gated behind the `alpha` feature gate. It appears in the marketplace picker and is creatable only on deployments where early-access features are enabled. Contact Redpanda support if you don't see SharePoint in the picker. +==== + +== 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 eight tools across sites, document libraries, lists, and files: + +[cols="1,2"] +|=== +|Tool |Description + +|`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. + +|`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. + +|`upload_file` +|Upload a file to a site's document library. Requires the `Sites.ReadWrite.All` scope. + +|`search_content` +|Search content across a site by keyword. + +|`list_lists` +|List the SharePoint lists on a site. + +|`list_list_items` +|List the items in a SharePoint list. +|=== + +The file tools operate on the site's own drive (`/sites/{id}/drive/...`), never on a user's personal OneDrive. + +== Prerequisites + +Before you create the server, make sure you have: + +* Access to a Microsoft SharePoint tenant. +* 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 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]. + +== 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: ++ +[source,no-highlight] +---- +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 OAuth Provider. + +[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. +==== + +== Add Microsoft Graph permissions + +In the app's *API permissions*, select *Add a permission > Microsoft Graph > Delegated permissions*, then add: + +[cols="1,2"] +|=== +|Scope |Why + +|`Sites.ReadWrite.All` +|Read and write everything the server touches: sites, document libraries, files, lists, and `upload_file`. For read-only deployments, use `Sites.Read.All` instead, which drops `upload_file`. + +|`offline_access` +|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. + +[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. +==== + +== 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. + +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. + +== 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]. 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`. + +== Create the server + +Create a new SharePoint MCP server in the Agentic Data Plane UI: + +. Open *MCP Servers > Create Server*. +. Pick *SharePoint* from the marketplace picker. +. 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*. + +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. + +=== Configure from the CLI + +[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"] } + }' +---- + +== 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: + +. 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. + +[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. +==== + +== Troubleshooting + +[cols="1,2"] +|=== +|Symptom |What to check + +|SharePoint isn't in the marketplace picker +|The server is gated behind the `alpha` feature gate. Confirm that early-access features are enabled on your deployment, or contact Redpanda support. + +|`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]. + +|`scope_upgrade_required` +|The server's `required_scopes` was extended after users had already consented. Users re-consent 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/...`). + +|`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. +|=== + +== Related topics + +* 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 a server's tools] diff --git a/modules/control/pages/budgets.adoc b/modules/control/pages/budgets.adoc index d8a530d..98b5f11 100644 --- a/modules/control/pages/budgets.adoc +++ b/modules/control/pages/budgets.adoc @@ -19,7 +19,7 @@ After completing these steps, you will be able to: A *budget* caps LLM spend over a recurring period. When an agent's spend for the period reaches the budget's hard limit, AI Gateway rejects that agent's next LLM request with `HTTP 429` until the period resets. A separate warning threshold fires before the cap is reached, so you can react before an agent is cut off. -ADP enforces budgets per agent. In a budget, an _agent_ is its service-account email: the same identity that appears as `user_id` in spend data. +ADP enforces budgets per agent. In a budget, an _agent_ is its service-account email: the same identity that appears as `agent_id` in spend data. === How a budget works @@ -140,7 +140,7 @@ You don't view spend on this page. The *LLM Providers* page, dashboard, transcri |Aggregated spend by *provider*, *model*, *user*, *agent*, or *provider type*, available through `GetSpendingBreakdown` for programmatic access. The dashboard's provider-breakdown widget covers the provider dimension. |=== -Every breakdown and time-series query reads from the same `SpendingFilter` shape: a time range plus optional `provider_name`, `model_id`, `user_id`, `agent_id`, or `organization_id` filters. Combine filters to scope a query (for example, "all spend on Anthropic for user `alice` in April"). You can break results down by provider, model, user, agent, or provider type; `organization_id` is a filter only, not a breakdown dimension. +Every breakdown and time-series query reads from the same `SpendingFilter` shape: a time range plus optional `provider_name`, `model_id`, `user_email`, `agent_id`, or `organization_id` filters. Combine filters to scope a query (for example, "all spend on Anthropic for user `alice` in April"). You can break results down by provider, model, user, agent, or provider type; `organization_id` is a filter only, not a breakdown dimension. For more expressive queries, `SpendingFilter` also accepts an AIP-160 `filter` expression that lets you combine and negate dimensions in a single string (for example, `provider_name="anthropic" AND model_id!="claude-sonnet-4-6"`). The convenience fields and the `filter` expression compose; populate one or both. The server compiles the result to a single SQL `WHERE` clause, which avoids per-dimension fan-out queries. @@ -149,7 +149,7 @@ For more expressive queries, `SpendingFilter` also accepts an AIP-160 `filter` e `total_tokens` is derived server-side from input + output + cache tokens. You don't need to compute it client-side, and the value is consistent across all four `SpendingService` methods. ==== -`user_id` and `organization_id` are populated automatically from the request's authenticated identity (the caller's email and organization), so spend is attributed without any setup on your part. +`user_email` and `organization_id` are populated automatically from the request's authenticated identity (the caller's email and organization), so spend is attributed without any setup on your part. [[query-spend-programmatically]] == Query spend programmatically @@ -179,8 +179,8 @@ For more expressive queries, `SpendingFilter` also accepts an AIP-160 `filter` e |`model_id` |Restrict to one model identifier (`claude-sonnet-4-6`, `gpt-5.2`, and so on). -|`user_id` -|Restrict to one identified user. Anonymous traffic is excluded. +|`user_email` +|Restrict to one identified user, matched on the caller's email. Anonymous traffic is excluded. |`agent_id` |Restrict to one agent: the service-account email recorded on the on-behalf-of path. Leave it empty to match every row, including direct user calls; set it to scope spend to a single agent. @@ -276,7 +276,7 @@ For the per-evaluator cost model and how it interacts with the dashboard's spend == Multi-tenant viewing patterns -The `SpendingFilter` exposes `organization_id` and `user_id`, so every dashboard query and every API call can scope to a single tenant or user. Use this to: +The `SpendingFilter` exposes `organization_id` and `user_email`, so every dashboard query and every API call can scope to a single tenant or user. Use this to: * See per-tenant spend in the dashboard's provider-breakdown view. * Pull per-user cost reports through `GetSpendingBreakdown`.