Skip to main content Announcing Tool Gateway MCP: the universal MCPRead the announcement
Guillaume Lebedel Guillaume Lebedel · · 7 min
An employee record as an agent receives it, with job title and start date readable and personal email and salary masked, next to a policy that applies to the onboarding group

Say Yes to the Copilot, Not to the Salary Field

Table of Contents

The onboarding team wants the copilot connected to Workday so that “when does Priya start and who is her manager” gets answered without a ticket. The security review takes one look at what the Workday connection can return, which includes compensation and home addresses, and the answer comes back as no. Or it comes back as yes with a written policy that nobody can enforce.

This is the conversation where a lot of the agent rollouts we see stall, and nobody in the room disagrees about what the agent should see. The rollout stalls because the only control the admin has is the app switch, and the app switch has no setting for “everything except salary”.

The unit of control is wrong

The enterprise AI platforms our customers connect through, ChatGPT Enterprise, Microsoft 365 Copilot, Claude and Gemini Enterprise among them, each give an admin a list of connected apps to allow. It is the right control for its job, which is deciding which systems the platform may reach at all. It says nothing about which fields, which tools within the system, or which people.

So the admin looks for the next place to put the rule, and finds three:

  • The system of record. Workday has security groups and Salesforce has permission sets, and both are excellent at what they were built for, which is humans in roles. Neither knows that the caller is an agent acting for a member of the onboarding team through a copilot, and each rule has to be repeated in every system the agent touches. Ten systems means ten rule sets, maintained by ten admins.
  • The system prompt. This is where most rollouts end up. “Do not read compensation data” in a system prompt is a request rather than a control. The rewardhacking.org dataset is 3,607 reports of agents doing more than they were asked, and the largest category is exactly this: the agent read the instruction and acted outside it anyway.
  • Custom middleware. A platform team builds it once, for one agent, and nobody wants to own it by the second quarter.
Where the rule livesUnit of controlKnows it is an agentKnows which memberMaintained
Platform app allowlistThe whole appYesYesOnce per platform
System of record permissionsField, per human roleNoOnly if the agent borrows a human loginOnce per system
System promptWhatever the agent decides to honourYesYesPer agent, unenforced
Gateway policyConnector, tool, input field, output field, valueYesYesOnce per team

Put the rule where the call passes through

Every call an agent makes to a business system passes through one point on its way there: whatever the agent uses to reach the system. In a copilot deployment that is the gateway that holds the connections. It sees which member the agent is acting for, which tool is being called, which arguments it carries and which fields come back. That is the only place all four facts exist at once, so it is the only place a rule can use all four.

A field policy at that point has a shape that an IT admin can write without a developer:

For the Employee onboarding group, deny reads of personal email and deny writes to compensation fields, on every HRIS connector.

Everything else about the record stays available. The agent gets the start date and the manager. The response comes back with the personal email masked rather than the whole call refused, because a refused read pushes the team to a workaround and the workaround is where the next incident starts. A write to compensation is refused in full, because an agent that silently writes the subset of an update it was allowed to write has produced a record nobody asked for.

Route of an agent request through the gateway checkpoint: member identity is resolved, tool and input rules are checked before the HRIS is called, and denied output fields are masked in the response

Two properties make this rule safe to add to a running deployment. A deny only narrows, and a matching deny wins over any allow, so it cannot grant a tool the member did not already have. The exceptions live inside the rule as named exemptions rather than in a competing allow that someone has to keep in sync.

The rule is also keyed to the group, so when HR moves someone from onboarding to payroll, the directory change moves the policy with them.

How we built the field layer

This is how Permission Policies at StackOne works, and two implementation decisions are worth knowing about even if you never use our version.

The first is that every policy compiles to a Cedar statement. Cedar is the authorization language AWS open-sourced in 2023, and its property we cared about is that forbid always beats permit, which is the “deny wins” rule above expressed by the engine rather than by convention. The onboarding rule looks like this once the builder has drafted it, one forbid per direction:

forbid (
  principal in StackOne::Group::"employee-onboarding",
  action == StackOne::Action::"readField",
  resource in StackOne::Category::"hris"
)
when { resource.path == "employee.personal_email" };

forbid (
  principal in StackOne::Group::"employee-onboarding",
  action == StackOne::Action::"writeField",
  resource in StackOne::Category::"hris"
)
when { resource.path like "employee.compensation.*" };

The second decision is that the resource path in those statements is a unified field path rather than a provider property. employee.compensation.* names the same field in Workday, BambooHR and HiBob, because the connector maps each provider’s own schema onto one model before the policy sees it. One rule covers the field across the 520+ connectors we ship, and a connector added next quarter inherits it. Writing the same rule against provider property names would have brought back the ten-systems problem in a different costume.

Two limits, so I do not oversell it:

  • Value conditions. “Deny payment amounts above $500” needs the argument typed and normalised before the policy sees it, and that plumbing is younger than the tool and field rules.
  • Member identity. It has to reach the call. A session created when a member connects their copilot carries it. A bare API key does not, so an integration calling with a project key is governed by connector scoping rather than by member policies. Keep that scoping in place.

The rule in the builder

Policy builder Active
Try a use case
Hide PII fields in tool responses and block requests that change those fields. Keep other fields within their existing permissions.
Who it applies to

Enforcement

Generated policyRead + write restrictions
Policy format
forbid (
  principal,
  action in [
    StackOne::Action::"readField",
    StackOne::Action::"writeField"
  ],
  resource in StackOne::Label::"pii"
);

The same rule in Cedar or JSON. Audience and mode are assigned separately.

Audience
Everyone
Mode
Enforce

PII field group

PII fields protected

PII fields are masked in responses. A request that changes a protected field is blocked in full. Other fields keep their existing access.

A representative example of the StackOne policy builder, not a captured session. The Protect personal data preset drafts a rule of the same shape from a plain-English description. Audience and enforcement mode are assigned separately from the rule, and switching to Monitor only keeps the same Cedar.

A field policy decides what the agent may see and change. It does not decide whether the request itself was planted. An HR record or a support ticket can carry text that reads as an instruction to the agent, which is prompt injection, and that needs a control of its own. Defender screens tool results before the agent acts on them, using a model that runs inside the platform, and StackOne publishes the 22MB open-source classifier behind it on GitHub.

What this changes for the rollout conversation

The security review stops being a yes or no on Workday. It becomes a short list of fields per team, which is a conversation HR and security have had before about humans and can have again about agents in an afternoon. If you are the one writing that list, start with the two fields that would end up in a regulator’s letter, personal contact details and compensation, and leave everything else at the access the team already has.

Keep the app allowlist. It is the outer fence and it should stay closed for systems nobody has a use for. The field policy is what lets you open it for the systems people need without opening the whole record.

If that list of fields is the conversation you are about to have, book a Permission Policies demo and we will write the first two rules against your HRIS with you.

Frequently Asked Questions

Can an AI copilot connect to Workday without exposing salary data?
Yes, if the permission policy sits at the gateway the copilot calls through rather than in the app allowlist. A rule for the onboarding group can mask personal email on reads and refuse writes to compensation while the start date, manager and job title stay available.
Where should field-level permissions for AI agents be enforced?
At the point every call passes through on its way to the business system. That is the only place the member identity, the tool, its arguments and the returned fields exist together, so it is the only place a rule can use all four. Permissions inside each system of record have to be repeated per system and do not know an agent is calling.
Do permission policies replace the app allowlist?
No. The allowlist stays the outer fence for systems nobody has a use for. Permission policies open an allowed system for the teams that need it without opening the whole record, by naming the fields and values inside it.

Put your AI agents to work

All the tools you need to build and scale AI agent integrations, with best-in-class connectivity, execution, and security.