Last updated
Wiring an agent up to SharePoint directly means giving it real permissions and hoping its tool calls stay well behaved.
Every quarter, someone in procurement has to confirm that each contractor working on your sites holds a current insurance certificate and a current safety certificate. The contractors are rows in a Suppliers list. Their certificates are PDFs in a Certificates library, and each file carries the supplier as a lookup, a certificate type and an expiry date. Done by hand, the check is an afternoon of filtering two views and pasting rows into a spreadsheet, which is why people now ask whether an agent could do it for them.
It could, provided it reads the data safely. Connecting the agent straight to SharePoint starts a security conversation that rarely ends well, because an agent with Microsoft Graph permissions can do whatever those permissions allow. Every tool call is also a live request against production SharePoint, and a runaway loop of calls can trip throttling for everyone else on the tenant.
The job you want done is smaller than that. The agent gathers the evidence and writes it up as a packet, then a person reads the packet and decides what happens next. Nothing in that job needs the agent to touch SharePoint.
Give the agent a database, and keep Graph for SPSync
SPSync copies the Suppliers list and the Certificates library’s metadata into a database and refreshes them on a schedule. The agent’s SQL tool talks to that copy and holds no Graph permissions of its own, so however many queries it runs, SharePoint never sees them.
The certificate check then runs as four reads against the replica:
- Describe the
suppliersandcertificatestables, which shows that each certificate’s supplier lookup is a key column,supplier_id. - Select the suppliers marked as active on site.
- Join them to
certificatesonsupplier_idand take the latest expiry date for each certificate type. - Return every supplier with a type missing or expiring before the next review, along with the item IDs behind each line.
Those results are the packet. The procurement lead reads it, using the item IDs to open any record they want to check in SharePoint, and chases the suppliers who are short. Any change that follows, such as taking a contractor off the rota or updating a supplier record, is made by that person in SharePoint under their own account. The agent has no write path to SharePoint. SPSync never writes to it, and no SQL statement against the copy can reach the source lists.
The check reads only the library’s own columns. On a live tenant the file name, URL, size and hash columns are not reliably populated, so the packet identifies each certificate by its item ID and the person opens it from there.
| Title | Client lookup | Tags multi-choice | Owner person | Due |
|---|---|---|---|---|
| Riverside | Acme Ltd | Design; Build | Ada Lovelace | 2026-11-30 |
| Harbour | Borealis plc | Build | Alan Turing | 2027-02-14 |
then sync
- id
- title
- client_id → clients.id
- owner_id → sp_users.id
- due
- is_deleted soft delete
- sp_modified
- id
- title
- …
- project_id
- tag
One row per value: Riverside has two.
- id
- display_name
One users table shared by every list.
The schema works for a SQL tool from the first query. Each list is a table. A lookup becomes a
key column, so “opportunities for this account” is a join, and a multi-value column becomes
a child table, so the agent never parses a delimited string. Deleted items keep their row
with sp_deleted_at set, which means a question about what changed gets an answer and the
record is still there. The walkthrough at the end of this page uses the same schema for a
sales assistant over two lists, Opportunities and Accounts.
Decide what the agent may see
The boundary is database administration you already know. Create a credential for the agent, grant it the tables or views it needs and no more, and add row-level security in Postgres where it should see only a region or a team.
On the SharePoint side, SPSync’s own connection can be limited with Sites.Selected to the
sites that hold the agent’s lists, so the copy never contains more than the agent’s job
requires. Replicate to whichever database the tool can already reach; Postgres or SQL
Server covers most cases. An agent in Azure AI Foundry has no SQL tool, and its SharePoint
tool reads documents, so
Azure AI Foundry agents and SharePoint list rows
walks through an Azure SQL replica, a read-only view and the MCP tool.
When the agent needs meaning as well as rows
Some questions are about wording: “find the deals that mention a competitor’s renewal”. For those, add pgvector or Azure AI Search as a second destination. Both are shipped, both generate embeddings on sync, and both sit beside the relational copy. See SharePoint to pgvector for the setup.
The worked example in MCP Server for SharePoint Data: Query SQL, Not Graph follows the sales assistant through it: Opportunities and Accounts replicated to Postgres, the read-only role and views the assistant gets, the Claude Desktop config, and the SQL it writes for pipeline questions.
When several agents need the same lists, an agent data layer for SharePoint sets out the whole pattern, from the four layers down to a three-list join costed against Graph and against the replica.
Desktop, Azure or command line
All three editions run the same engine, write the same tables and use the same licence key, so the choice comes down to who looks after the sync.
Desktop app
For macOS and Windows. Sign in with Microsoft, tick the lists, choose a database and sync from your own machine on the schedule you set. Delegated sign-in needs no admin unless your tenant requires admin consent for apps.
In your Azure
SPSync in your Azure, a managed application for your own subscription, is deployed with you on a call while the Marketplace listing is prepared. Once deployed it is always on, so the sync keeps running with every laptop closed.
Command line
Every replication is a YAML file you can review and commit. Run it from a server, a container or a CI pipeline, and re-run it safely any time.
Set it up
Ready to run this against your own site? The setup guide takes you from install to a first replication, and the SharePoint connection guide covers signing in.
Script it
Everything you can click, you can script. The CLI, YAML config and automation below reproduce this scenario exactly. See the CLI reference.
spsync auth login --client-id 00000000-0000-0000-0000-000000000000
spsync export-config --output replication.yaml --pipeline-name ai_agents_tools_over_sharepoint --prefix ai_agents_tools_over_sharepoint --destination-kind postgres --connection-string-env SPSYNC_DB_CONNECTION --dataset-name ai_agents_tools_over_sharepoint --client-id 00000000-0000-0000-0000-000000000000 --site-url https://contoso.sharepoint.com/sites/example --all-lists
spsync doctor replication.yaml
spsync run replication.yaml pipeline_name: ai_agents_tools_over_sharepoint
prefix: ai_agents_tools_over_sharepoint
auth:
mode: delegated
client_id: 00000000-0000-0000-0000-000000000000
tenant_id: common
destination:
kind: postgres
connection_string_env: SPSYNC_DB_CONNECTION
dataset_name: ai_agents_tools_over_sharepoint
scopes:
- site_url: https://contoso.sharepoint.com/sites/example
mode: entire_site
reconcile_interval_days: 7
create_reporting_views: false
Cron (PostgreSQL nightly):
# every night at 02:00
0 2 * * * cd /opt/spsync && SPSYNC_DB_CONNECTION="$(cat /etc/spsync/db.env)" uv run spsync run replication.yaml >> /var/log/spsync/run.log 2>&1 GitHub Actions:
# .github/workflows/spsync-run.yml
name: SPSync
on:
schedule:
- cron: "0 2 * * *"
workflow_dispatch: {}
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
- run: uv sync
- run: uv run spsync run replication.yaml
env:
SPSYNC_DB_CONNECTION: ${{ secrets.SPSYNC_DB_CONNECTION }}
SPSYNC_LICENCE_KEY: ${{ secrets.SPSYNC_LICENCE_KEY }} Azure DevOps:
# azure-pipelines.yml
schedules:
- cron: "0 2 * * *"
branches: { include: [main] }
always: true
steps:
- script: uv sync
- script: uv run spsync run replication.yaml
env:
SPSYNC_DB_CONNECTION: $(SPSYNC_DB_CONNECTION)
SPSYNC_LICENCE_KEY: $(SPSYNC_LICENCE_KEY) Windows (PowerShell + Task Scheduler):
# Windows Task Scheduler action (see /docs/cli/automation/)
$env:SPSYNC_DB_CONNECTION = (Get-Content C:\spsync\db.env)
uv run spsync run C:\spsync\replication.yaml
schtasks /Create /SC DAILY /ST 02:00 /TN "SPSync" /TR "powershell -File C:\spsync\run.ps1" macOS (launchd):
# ~/Library/LaunchAgents/io.spsync.run.plist, then: launchctl load ~/Library/LaunchAgents/io.spsync.run.plist Secrets (licence key, connection strings) come from the environment, so never write them into the YAML. See scheduling and secrets.