Governed MCP for the software you already run · EU-hosted · Every call logged

Don’t ban it. Govern it.

Your people are already putting company data into AI. You can’t see it, can’t filter it, and can’t prove it didn’t happen. A ban doesn’t stop that — it just moves it somewhere you can’t look. Sallyport puts one gate in front of the systems you already run: every call checked against that person’s rights, filtered against your DPO’s classification, and written to a log nobody can quietly edit — including us.

EU-hosted GDPR / AVG EU AI Act SSO · SCIM · MFA Append-only log SIEM export

The exposure you already carry

Your team already uses AI.
You just can’t see it.

Somewhere in your company this week, customer data will be pasted into a free chatbot — unlogged, unfiltered, outside the EU. Not out of malice; because it works. A policy doesn’t stop that, and a ban only moves it somewhere you can’t look. Sallyport doesn’t fight the habit. It gives it a home you can see into.

Tonight, 23:41 — without Sallyport
to: some-free-chatbot.example “Here’s our client list with emails, phone numbers and what they owe us — write me follow-up emails: klanten_export_final2.xlsx”
✗ no log — nobody will ever know
✗ no filter — the whole file went
✗ no EU hosting — and no way back
✗ no answer to “did this happen?” — the only question you’ll be asked
Tomorrow, 09:05 — with Sallyport
to: sally · governed “Write follow-ups for my clients from last week’s quotes.”
✓ his clients only — rights applied
✓ personal data withheld — DPO’s rules
✓ EU-hosted — nothing left the region
✓ logged — you can answer the question
67%of employees already use AI at work
18%of companies have any AI usage policy
30 secto wire an assistant to your ERP over MCP
1leaked client list you can never un-send

The safe path has to be the easier path. That’s the whole trick.

The gate, live

Same stack. Same role. Different answer.

The demo worth watching is the one that says no. Access follows the person, personal data follows the DPO’s classification, and the log follows every call — including the refused ones. Compliance you can demonstrate, not declare. Pick who’s asking.

GDPR / AVG EU AI Act ready Data minimisation Every call logged
sally · connected to moneybird, gripp, exact · studio vandaag
AUDIT LOG  

Both men hold the same role. David asks for a field the DPO classified as personal data and is refused; Thomas asks for business data his rights cover and gets it. Both calls are logged identically — the refusal is the product, and the record of it is what you will actually be asked for.

What a filtered answer looks like

The same record, twice.

On the left, the record as it sits in Gripp. On the right, what Sales actually receives through the gate — same request, same moment.

Raw record in Gripp
name:       Nova BV
email:      finance@nova.nl
iban:       NL91 ABNA 0417 6789
cost_price: 84.00
status:     inactive
What Sales receives
name:       Nova BV
email:      finance@nova.nl
iban:       •••• •••• 6789   masked
cost_price: (hidden)        hidden
status:     active           inactive rows withheld

Masked, hidden and withheld are three different things, and the difference matters to a DPO: a masked field proves it exists, a hidden one doesn’t, and a withheld row was never in the answer at all.

Before the rules, the roles

Two kinds of role, and nobody gets both by accident.

Everyone carries two independent sets of rights, and conflating them is how least-privilege quietly dies.

Connector roles

Which business data they can reach.

Sales, Support, Finance. Scoped per connector, and the thing every rule on this page is written against.

Sallyport roles

Which platform powers they hold.

Admin, Auditor, DPO. Configuration, read-only audit access, or signing off on requests for personal data.

Someone can have either, both, or neither — and an Auditor with no connector role can read the log without ever reading a customer record.

Governed MCP

Deny is the default.

Three checks stand between an assistant and your data, and none of them is a promise in a policy document. Everything a role can reach is set on one screen, where you can see the whole of it at once.

01

Who is asking.

Identity comes from your own IdP over SSO, with SCIM keeping the roster in step. Rights are resolved fresh on every single request — never carried around inside a token that outlives the decision. Revoke someone in your IdP at 14:00 and the call they make at 14:01 is already refused.

02

What they may reach.

A declared allowlist: per role, per table, per field, per operation — plus the row filter that decides which rows, which read and write state separately. Not code somewhere that a well-meaning change could widen. A field is reachable only because somebody chose to grant it. Absence is denial.

03

What happened.

Every call — allowed, filtered or refused — is appended to a log that nobody can quietly edit, us included. It ships out as NDJSON to Splunk, Datadog, Sentinel, Elastic or S3, so the record lives in your estate and not only in ours.

app.sallyport.eu/rules/sales/gripp/entities

Sales × Gripp · entities

Set each entity’s access level — one table per group
Copy from roleLoad from preset
CRM 3 of 9 entities
EntityAccessRules
ContactsNo accessViewEditManagedate of birth masked
CompaniesNo accessViewEditManageown accounts only
NotesNo accessViewEditManage+ rule
Finance 2 of 8 entities
EntityAccessRules
InvoicesNo accessViewEditManageowner = the person asking
PaymentsNo accessViewEditManage—

Every entity, every role, one screen — and a newly connected entity arrives at No access until somebody chooses otherwise. When you are asked who can reach personal data in which system, this page is the answer — not a spreadsheet somebody maintained until they left.

scroll the tables →

And no business records are stored. Sallyport calls your tool’s own API, live, on every request. There is no warehouse, no nightly sync and no copy of your data sitting here to be breached — which is also why turning it off leaves nothing behind.

How deep it goes

Which tables is the easy half. Which rows and which columns is the deep end.

Handing a role an entity is one decision out of five. The three below it are the ones that decide whether an assistant comes back with one customer record or forty thousand — and they are the three a plain MCP server does not have.

  1. 01

    Connector

    Which system this role can reach at all. Sales gets Gripp; Exact is not on the list, so there is nothing to misconfigure.

  2. 02

    Entity

    Which tables inside it, at which level — No access, View, Edit, Manage. A newly connected entity arrives at No access and stays there until somebody chooses otherwise.

    most MCP servers stop here
  3. 03

    Operation

    Read and write are separate grants with separate scopes. A role can read every open invoice in the company and be able to change none of them — including its own row filter, which is allowed to differ between the two.

  4. 04

    Row

    Which records come back at all. Conditions are evaluated per request, so they can be written relative to whoever is asking — and they can reach across a relation, into a field on the linked record.

  5. 05

    Field

    Which columns survive: returned, masked, or dropped before the response is assembled. Per role, on top of the classification your DPO gave the field.

The three marked in green are the ones a plain MCP server has no way to express — it has no notion of who is asking, so it cannot scope a row or drop a column on their behalf.

app.sallyport.eu/rules/sales/gripp/contacts/rows

Row filter · contacts

Which records come back at all

Return rows where all conditions match. The rest are withheld on the way out — and pushed down into the vendor’s own query wherever the vendor can express them, so they are never fetched in the first place.

wherestatusisactive
andowneristhe signed-in user
andregionis one ofEU
andcompany.credit_holdisfalse

Conditions the vendor’s own API can express are compiled into the call it receives. The rest are applied here, against the response, before it is assembled.

pushed into the call to Gripp GET /contacts?status=active&owner=8412&region[in]=EU applied here, on the way out company.credit_hold = false
read212 of 540 rows returned · 328 withheld writeits own filter — owned rows only, and never the ones on credit hold
app.sallyport.eu/rules/sales/gripp/contacts/fields

Field policy · contacts

Which columns survive the response

Every field arrives carrying the classification your DPO gave it and the default that follows from it. A role can be narrowed below that default freely; widening past it takes a signed exception with an expiry date.

bsn Special VisibleMaskHide not present in the response
name Personal VisibleMaskHide Nova BV
email Personal VisibleMaskHide finance@nova.nl
phone Personal VisibleMaskHide +31 20 555 ••••
iban Financial VisibleMaskHide NL•• •••• •••• 6789
cost_price Financial VisibleMaskHide not present in the response
status Public VisibleMaskHide active
Mask keeps the shape, and shape is information. A masked BSN still gives away its length and its check digit, which is why the default for special-category data is Hide — dropped while the response is being built, rather than blanked after it exists.
The record, as Gripp holds it
name: Nova BVemail: finance@nova.nlphone: +31 20 555 0142iban: NL91 ABNA 0417 6789cost_price: 84.00bsn: 123 45 689status: active
The record, as Sales receives it
name: Nova BVemail: finance@nova.nlphone: +31 20 555 ••••iban: NL•• •••• •••• 6789cost_price and bsn — no key, no null, no tracestatus: active… and 328 of the 540 rows are not in this response at all
This is the half that is hard to build, and the half everyone skips. Checking who is calling and then forwarding the request unchanged is one of the five decisions above; it is also the only one most gateways make. Every level here is declared rather than coded, versioned in your workspace, and enforced on the way out — identically for the REST API, the MCP endpoint and anything playbooks run, because all three come out through the same gate. Including the computed answers: a total folded out of 128 invoices is masked by the same rule that masks the column it was summed from.

The difference

One token for everybody is not access control.

MCP is the standard that lets an assistant call your systems. Most MCP servers implement the calling half and stop there. That half is the easy half.

A plain MCP server
  • ✗ One integration credential, with the integration’s full rights, shared by everyone who can reach it.
  • ✗ The assistant asks; the API answers. Nothing sits in between deciding whether this person should see this row.
  • ✗ Sales, finance and the intern get identical results, because the server cannot tell them apart.
  • ✗ The vendor key lives wherever the integration put it, and often comes back out again.
  • ✗ When someone asks what was accessed last quarter, there is nothing to show them.
The same protocol, through Sallyport
  • ✓ The caller’s own identity, from your IdP — not the integration’s.
  • ✓ Their rows and their fields, decided per request against the declared contract.
  • ✓ Personal data withheld by the DPO’s classification, whoever is asking and whatever they ask.
  • ✓ The vendor key sealed with a one-time data key, never returned to any caller — it only ever leaves as a call to the vendor.
  • ✓ Every call recorded, refusals included, and exportable to your SIEM.

MCP is a public protocol, not ours — any assistant speaks it, and you can point yours back at the raw API whenever you like. What we add is the half that decides.

Evidence

“Show me every AI call that touched personal data this month.”

This is the request that arrives from an auditor, a customer’s security team, or your own board after something goes wrong. Today it takes a week and ends in a best guess. Filter the log.

audit log · studio vandaag · 7 of 7 calls
TimeWhoRoleCallFieldsVerdict
09:02:11melanieAdmin moneybird.invoices.listtotal, due_at allowed
10:42:07thomasSales moneybird.invoices.listtotal, state owner=thomas allowed
10:44:31davidSales crm.contacts.listdate_of_birth refused
11:15:58hannahDPO crm.contacts.getdate_of_birth, email allowed
13:07:20annaFinance exact.journals.list7 fields allowed
15:31:02davidSales crm.contacts.exportbulk export refused
16:48:44thomasSales moneybird.mail.bulk142 recipients held
EXPORT  append-only · NDJSON → Splunk, Datadog, Sentinel, Elastic, S3

scroll the table →

Robert de Wit is an Auditor: read-only, across every connector — the reason that role exists. Note that the two refusals are in the log at all. A gate that only records its successes is a gate that cannot answer the question you bought it for.

Classification

One classification. Every future call. Every tool.

Hannah is the DPO. She marks ‘date of birth’ as personal data once, in one place, and the gate withholds it from every connector, on every call, from that moment on — without anyone touching Moneybird, Gripp, Exact or the CRM.

Set once, enforced everywhere.

Classification lives at the gate, not in each system’s own settings. That means it applies to the connector you add next month too, before anyone has thought to configure it.

Exceptions expire on their own.

Someone genuinely needs a classified field for a fortnight. Grant it time-bound; it lapses without anyone having to remember. Standing exceptions are how least-privilege quietly dies.

Refusals are visible to her.

Every request the classification blocked shows up in the DPO’s own view — which turns “is our policy working?” from an opinion into a count.

gripp · data map · 8 entities · 116 fields scanned
FieldClassificationNetherlands (AVG)Germany (BDSG)France (loi I&L)
employee.socialsecuritynumberSpecialblocked  BSN · Steuer-ID · NIR — never reaches AI
employee.dateofbirthPersonalfilteredmaskedmasked
contact.emailPersonalmaskedmaskedmasked
employmentcontract.internal_price_per_hourFinancialFinance only
company.companynamePublicallowed

Sallyport reads a tool’s shape before AI touches it and flags every field that falls under privacy law — Dutch AVG, German BDSG, French loi Informatique et Libertés. New fields arrive unclassified and nothing reaches AI until the DPO signs off.

The half hour she spends on this is the half hour the rest of the year rests on.

Exceptions

Yes, for seven days, and then it stops.

A “no” that can never become a “yes” is a rule people route around. An exception is a temporary override that supersedes the standing rules — it can grant or revoke, on one entity or one field, for a window you set. Then it reverts on its own.

exceptions · studio vandaag · 4 active
WhoChangeScopeUntilState
David · Salesgrant read-onlygripp · contacts.birthdate21 Jul · 7 dayspending DPO
Anna · Financegrant Viewexact · invoices30 Julactive
Contractorsrevoke all accessgripp · contacts31 Julactive
Supportunmask ibanmoneybird · contactsexpiredreverted

Nobody has to remember to take it away. Standing exceptions are how least-privilege quietly dies — so here they can’t stand.

Residency & custody

The four questions, answered before you ask them.

These are the questions that come before every pilot, in the order they always get asked. Here they are without the discovery call.

Where does the data live?

In the EU. Hosted in Germany. Nothing leaves it, and we never train on any of it.

What do you keep?

Metadata and which rows a rule held back — not full record payloads. You set how long the audit log and the result cache live, and you can purge on demand without opening a ticket.

Are you a sub-processor?

Yes, and a transparent one. You stay the controller; we’re the processor. There’s a signed DPA with EU SCCs and a published list of everyone we rely on.

Can I prove any of it?

That’s the whole product. Every request is recorded — who asked, what they got, which rule held what back — in an append-only log nobody can quietly edit, including us.

The objection

“You’re one more place our client data can leak.”

Fair. So the vendor keys you give us are sealed with a key you also hold — drop ours and you revoke our access for good. Each secret gets its own one-time data key, and that key is itself encrypted. Plaintext exists only in memory, for the single request that needs it, inside a hardware boundary. No operator, no database dump and no log ever sees it. We are a sub-processor that cannot read your secrets.

GDPR-readyDPA on request, EU legal basis, and you stay the controller throughout.
EU AI Act alignedThe per-call audit trail and human-set access rules map onto the transparency and oversight obligations coming into force.
EU data residencyHosted and processed in the EU, in Germany.
We never train on your dataNot ours to train on, and not used for it.
Retention you controlResult cache windows are yours to set per function, and what an AI model is allowed to keep is a switch in your own settings.
SOC 2 — in progressType II is underway. The security posture is documented and available under NDA today, so a review needn’t wait for the badge.

Additive, not a project

No migration means no new attack surface.

The usual answer to “we need AI on our data” is a platform you migrate into — a year of work, a new data store to secure, and a copy of everything you own living somewhere it didn’t before. This is the other answer.

0

Systems replaced. Your accounting, projects and CRM stay exactly where they are, with the vendors you already assessed and the contracts you already signed.

0

Copies of your data created. Nothing is exported, converted or re-entered. There is no export to lose, no warehouse to breach, and nothing to delete if you leave.

1 afternoon

To the first governed call. Connect a system, review the roles it proposes, classify what’s personal, and watch the log start filling. Not a quarter — an afternoon.

Disconnect and nothing moved, so there is nothing to move back. That is a small decision, and small decisions are the only kind worth making about something this new.

And then it does the work

The same gate is what makes automation safe to switch on.

Governance that only ever says no gets switched off. The reason this one survives contact with the business is that the same gate is what lets the business have the thing it actually wants.

A playbook inherits its owner’s rights

An automation can never reach further than the person who set it running. There is no service account quietly holding more than anyone should.

A human step waits where you put one

Sign-off is part of the definition, not a convention people follow when they remember. No sign-off configured, no pause; one configured, it waits.

Every step lands in the same log

Automated work is audited exactly like a person’s. When something runs wrong at 03:00 you can still say what happened and who approved it.

Sending this to whoever signs? There’s a version of this page written for them — what it does for the business, what it costs, and what it saves. See what it does for the business →

More cases

A week through the gate.

Same company, same stack, seven different answers — because seven different people asked.

DV
DavidSales

Export the whole customer database to a spreadsheet.

Refused · bulk export

policy: data minimisation · logged & visible to Hannah (DPO)

HB
Hannah BosDPO

Reclassify ‘date of birth’ as personal data on every connector.

Applied · gate-wide

one classification → enforced on every future call, all tools

RW
Robert de WitAuditor

Show every AI call that touched personal data this month.

Allowed · audit scope

audit.log.query · read-only role · the reason the Auditor role exists

TB
Thomas BakkerSales

Email all my clients the new-year discount offer.

Routed · needs sign-off

bulk send → approval from Melanie (manager) · drafts held at the gate

SK
Sophie KoningSupport

What did De Groot Media order from us last quarter?

Allowed · read-only

gripp.projects.list · her accounts only · no financial fields

AM
Anna MeijerFinance

Which payments from last week are still unmatched in Exact?

Allowed · finance scope

exact.journals.list · 7 rows · full ledger access, her role’s job

SA
Playbooksstarted by Melanie

Onboard Lisa de Boer, starting next Monday.

Running · 4 of 7

gripp + moneybird + mail · sign-off by Julia · rest runs Mon 08:00

Bureau Noord · IT

“I spent a year trying to ban it, and lost. Now every call our people make is one I can show the auditor.”
DD
Daniel Dekker IT Lead, Bureau Noord

Free trial · EU-hosted · Every call logged

Turn shadow AI into
logged AI.

Connect one system this afternoon and watch the log start filling. If it’s not for you, disconnect — nothing moved, so there’s nothing to move back.