IT
How can we restrict a shared-key MCP connector to each user's own data?
When an MCP connector uses one organization API key, request/response hooks can inject the signed-in user's email or employee ID into tool calls and filter results—without changing the upstream server.
- integrations
- mcp
- security
- endpoint-ai
Some third-party APIs only support a single organization API key—powerful enough to read or change data for the whole tenant. That key is configured only by IT in Company settings; employees never receive it directly. Harriet can still expose those tools safely when you combine per-user Harriet authentication, least-privilege tool permissions, and request/response hooks: small JavaScript handlers that run on each allowed tool call in Harriet chat and workflows.
Hooks are not a replacement for vendor-side per-user authorization. They are a compensating control you configure in Company settings so each call is scoped to the signed-in Harriet user.
When this pattern fits
Use shared-key credentials plus hooks when:
- The upstream API accepts filters by email, user principal name (UPN), or employee ID, or
- List/detail responses include an owner email (or equivalent) on each row that you can match against the current user.
Prefer per-user OAuth or native vendor RBAC when the product supports it—see MCP authorization and stored credentials. Use hooks when you must keep one org key but still limit what Harriet returns to each person.
If the API only exposes opaque owner IDs and requires a separate lookup to map email → ID, hooks alone are usually not enough—see How do we adapt an existing API into an MCP connector? for wrapper and join patterns.
What hooks can do
Harriet runs hooks in this order for each tool call (chat and workflow agents):
- Tool permission check (roles, groups, enabled tools)
- Preprocess — modify tool arguments before the upstream call
- Upstream call — Harriet uses the organization secret server-side (end users never hold this key)
- Postprocess — modify or filter the response before Harriet shows it to the user
Each handler receives a read-only context with fields such as:
context.user.email— the signed-in user's emailcontext.user.id— Harriet user IDcontext.user.employee_id— employee ID when set on the profilecontext.roles.*— Harriet role flags (for example pay-data access)context.group_ids/context.group_names— customer groups
Handlers are keyed by tool name. Return modified arguments or results, or throw an error in preprocess to deny a call.
Configure hooks in Company settings
- Create or open the MCP connector (New connector — see How to create an MCP connector (Company settings)).
- Store the organization API key in the connector authentication step (IT only—not shared with employees).
- Sync tools so Harriet discovers tool names.
- On the connector configuration page, enable Request / response hooks.
- Use Generate stubs from synced tools, then edit the JavaScript for the tools you need.
- Test hook with sample JSON for preprocess (arguments) and postprocess (typical response shape).
- Save, enable only the tools users need, and set tool permissions (groups, confirmation for writes).
During rollout, turn on Store tool arguments and results in audit logs under Harriet Endpoint AI Settings if you need to compare live payloads with your hook logic (requires audit permissions to view).
Example: MDM device inventory (Kandji-style)
Many MDM APIs list enrolled devices and include the assigned user's email on each device row. Some also accept a user email query parameter so the upstream list is already narrowed.
Suppose the synced tool is named list_devices and accepts optional user_email. A hook script can force the current user's email in preprocess and filter again in postprocess as defense in depth:
module.exports = {
preprocess: {
list_devices(context, args) {
// Always scope to the signed-in Harriet user — ignore model-supplied email.
return { ...args, user_email: context.user.email };
},
},
postprocess: {
list_devices(context, result) {
const email = (context.user.email || '').toLowerCase();
const rows = Array.isArray(result) ? result : (result.devices || []);
const filtered = rows.filter((device) => {
const owner = (device.user_email || device.email || '').toLowerCase();
return owner === email;
});
return Array.isArray(result) ? filtered : { ...result, devices: filtered };
},
},
};
Adjust field names (user_email, devices, nested arrays) to match your vendor's JSON. For OpenAPI / Swagger connectors, postprocess often receives a wrapper object with a result property containing the API body—filter result (or result.devices) instead of the top level.
Similar patterns work for other inventories that expose user email on each row (for example Jamf userAndLocation.email or Microsoft Intune emailAddress / userPrincipalName on managed devices), as long as you align handler logic with the synced tool names and response shape.
Guardrails
- Org key blast radius: The secret can still access all tenant data if hooks are wrong or bypassed. Review every enabled tool; deny tools you cannot scope. The org key must remain in Harriet configuration only—never in user-facing clients.
- Per-user access, not shared keys: Employees use their Harriet login (chat/workflows) or a per-user Endpoint AI MCP proxy credential on enrolled devices. Subscoped tool permissions define which endpoints each person may call; hooks further narrow data where needed.
- Do not rely on postprocess alone for writes: Preprocess should inject scoping into mutating calls; postprocess filtering does not stop a mis-scoped upstream write.
- Combine with tool permissions: Restrict which groups may invoke the skill; require confirmation on destructive tools in workflows.
- Test as multiple users: Validate with accounts in different groups so one user never sees another's devices or records.
- Hooks in Harriet chat and workflows: Request/response hooks apply when skills run in Harriet's cloud agent path. Endpoint AI desktop MCP uses Harriet's per-user proxy credential and tool permissions; request/response hooks may not apply on every desktop path—confirm in your deployment or combine with tight tool permissions and hooks tested in chat first.
- No network inside hook scripts: Hooks cannot call a second API or Harriet database lookup. Use
context.user.email,context.user.employee_id, and fields already in the tool arguments or response.
Related articles
See How do we adapt an existing API into an MCP connector? when you need OpenAPI setup or email-to-ID joins, How do we turn MCP tools on or off for specific groups? to limit which endpoints each audience can call, MCP authorization and stored credentials for OAuth vs org secrets, How do MCP tools work inside workflow agents? for confirmation and sub-agent rules, and How do we connect external tools using MCP? for the overall connector model.
Use Harriet in your organisation for searchable help, AI assistance, and your company knowledge base.
Log in to Harriet