Back to blog
Enterprise AI

Agent identity for Claude Tag

Chase Crumbaugh

Chase Crumbaugh

September 14, 2026 · 15 min read

Agent identity for Claude Tag

TL;DR

Connecting Claude Tag to any MCP or 3rd party service through a shared service account gives every Slack user the same permissions and attributes every action to "Claude". Speakeasy fixes both by giving the agent its own principal with one human owner, recording the agent as actor and the Slack user as operator on every call, and resolving permissions per call as the intersection of the agent's policy and the invoking user's. Workload identity federation covers agents with no human in the loop, and the MCP gateway is where all of it is enforced in the Speakeasy AI Control Plane.

Claude Tag brings Claude into the place your team already works. Mention @claude in a Slack thread and it can summarize the discussion, draft a reply, or file the Linear ticket everyone agreed on. For the small, repetitive work that clogs an engineering team's day, it's a real improvement.

Then someone asks the obvious question. Is Claude allowed to create tickets in Linear? Is the person who mentioned it? And is Claude even connected to Linear yet?

Answering those questions well is the difference between a Slack bot that a security team tolerates for one channel and an agent that can be deployed org-wide. This post walks through the gap a shared service account leaves, and how Speakeasy closes it with agent principals, on-behalf-of audit trails, least-privilege permissioning, workload identity federation, and the MCP gateway.

Why a shared service account breaks down

The recommended way to give Claude Tag access to Linear is to create a Linear account for Claude, generate an API key for that account, and paste the key into the Slack app's configuration. It works, and it is how most teams start. Two problems show up immediately.

As who? The API key carries whatever permissions the Claude account has in Linear. Every Slack user who can mention @claude now has those permissions, whether they are a staff engineer or a contractor who joined last week. The only levers are to accept that, or to shrink Claude's permissions down to the least any employee should have, which makes the agent useless for the people who needed it most.

On whose behalf? Linear records every action as "Claude". When someone asks the agent to close every open bug in a project, the audit trail says Claude did it. The person who gave the instruction is nowhere in the record. Reconstructing it means correlating Slack message timestamps with Linear's activity log by hand.

Neither problem is specific to Claude Tag or to Linear. Any agent that reaches a downstream system through a shared credential has the same shape, and the same shape blocks enterprise rollout. The fix has three parts: give the agent an identity of its own, record who was driving it on every call, and resolve permissions per call rather than per credential.

What is an agent principal?

An agent principal is a nonhuman identity that exists alongside users and roles. Instead of creating claude@yourcompany.com in Linear, then again in Jira, then again in GitHub, and assigning permissions in each, you create the agent once in Speakeasy under Agents. The agent gets a stable identifier of the form agent:<id>, and everything downstream refers to it by that identifier.

The agent's page in Speakeasy shows the durable facts at the top (the principal, its owner, and its lifecycle state) and, below them, the most the agent may ever be delegated.

Screenshot of an agent named Claude Tag in Speakeasy. The Identity section shows the principal identifier, the owner Chase Crumbaugh, and an active lifecycle badge. The Permissions section explains that adding a permission grants nothing on its own and that each API key is narrowed again at issuance against the owner's live permissions, with one MCP access grant, mcp:connect, applied to all servers.

Three properties distinguish an agent principal from a service account with a friendly name.

  • It has exactly one human owner. The owner administers the agent (renames it, suspends it, issues and revokes its credentials, transfers it to someone else) but does not lend it any permissions. An agent can hold permissions its owner lacks, and the owner cannot act through the agent to gain permissions they lack. That separation is what stops an agent from becoming an escalation path.
  • Its permissions are its own. Policy is attached directly to the agent principal: which MCP servers it can reach, which tools within them it can call, and which it is denied. Assign those once, and they apply wherever the agent is invoked, whether from Slack, from a CI job, or from a developer's terminal.
  • Its credentials are independently revocable. An agent API key or session is bound to the agent, expires by default after 90 days, and carries an immutable ceiling approved by the human who issued it. Revoking the agent revokes every credential it holds. Suspending it pauses them without losing anything.

When the owner leaves the company, the agent does not silently keep running on a departed employee's authority. Every request is blocked until an administrator reassigns ownership, and reinstating the former owner does not clear that block. Offboarding a person offboards the agents they were responsible for, which is the behavior an auditor expects and a shared API key never provides.

How does an on-behalf-of audit trail work?

Once the agent has an identity, the second question becomes tractable. Every call through the control plane records three distinct relations.

  • The actor is the agent principal. When Claude Tag creates a Linear ticket, the actor is agent:claude-tag, never the owner and never the person who typed the message.
  • The operator is the human driving the agent on that call. In Slack, that is the user who mentioned @claude. The operator is recorded for attribution and is never used to widen what the agent may do.
  • The authorizer is the human who approved the credential the agent is using. It is fixed for the life of the credential, so the approval decision survives even if that person later changes roles.

Going back to the example, on-behalf-of authentication with Linear means the activity log shows that it was Chase who instructed the agent to close every open bug. The ticket was created by the agent, at Chase's direction, using a credential that a named administrator approved. All three facts are on the record without anyone correlating timestamps.

The same lineage applies when agents call other agents. A subagent spawned by Claude Tag records its parent as the operator, so the chain from the original Slack mention to the last tool call stays intact.

How does least-privilege permissioning resolve conflicts?

The fundamental challenge with Claude Tag, and with any shared agent, is that shared access can exceed the requester's authority. The fix is to resolve permissions per call as the intersection of everything that has a say.

When Chase asks Claude Tag to do something in Linear, the call goes through only if all of the following allow it:

  • the credential's approved ceiling, fixed when the key or session was issued
  • the agent's current policy, which an administrator can change at any time
  • the permissions of the operator, Chase, as they stand at the moment of the call

Two examples show why each gate matters on its own.

Suppose an administrator has given Chase the Linear superadmin role but has decided Claude Tag should never take destructive actions. Chase asks the agent to delete a project. The agent's policy denies the call, and Chase's superadmin role does not override it. Chase can still delete the project directly in Linear, but not through the agent.

Now suppose the reverse. Claude Tag has broad permissions, and Chase has been flagged as a risky actor with destructive permissions revoked. Chase asks the agent to delete the same project. Chase's own permissions deny the call, and the agent's broad policy does not override it. The agent cannot launder authority that the person invoking it does not have.

Removing a permission takes effect on the next call. Restoring one reactivates the operation as long as it stays inside the credential's original ceiling, so tightening and loosening policy never requires rotating keys.

The practical result is that a fully permissioned Claude Tag can be deployed into Slack org-wide. Each user's role assignments are respected on every call, so one deployment serves the whole company with per-person scope rather than a per-channel compromise.

What about agents with no human in the loop?

Claude Tag has an operator on every call because a person types the mention. Plenty of agents do not. A scheduled Claude run that triages the on-call queue overnight, a CI job that opens a pull request, an autonomous agent runtime on a Kubernetes pod: none of these has a human to click "Allow" on an OAuth screen, and none has a good place to keep a long-lived secret.

Workload identity federation handles the autonomous case. The platform the agent runs on already hands it a short-lived, cryptographically signed token. GitHub Actions issues OpenID Connect (OIDC) tokens to every job, Kubernetes projects service account tokens into pods, SPIFFE issues SVIDs, and GCP and AWS both expose workload identity. Speakeasy accepts those tokens directly.

The sequence below shows an autonomous workload reaching an MCP server with no Speakeasy-issued secret anywhere in the loop.

sequenceDiagram
    autonumber
    participant W as Workload<br/>(CI job, pod, agent runtime)
    participant AS as Speakeasy<br/>token endpoint
    participant I as Trusted issuer<br/>(OIDC)
    participant GW as MCP gateway
    W->>AS: platform-issued JWT as a jwt-bearer assertion
    AS->>I: fetch discovery document and signing keys
    I-->>AS: JWKS
    AS->>AS: verify signature, issuer, audience, expiry, and claim matchers
    AS-->>W: short-lived access token bound to the agent principal
    W->>GW: tool call with the access token
    GW-->>W: MCP response

The workload posts its platform token to the Speakeasy token endpoint using the jwt-bearer grant from RFC 7523, the same mechanism the MCP community's SEP-1933 proposal defines. Speakeasy resolves the issuer through OIDC discovery, validates the signature against the issuer's published keys, checks the audience and expiry, applies the claim matchers an administrator configured (this repository, this branch, this namespace), and returns a short-lived access token bound to the agent principal.

Three things follow from that design.

  • No secret to leak. There is no Speakeasy-issued key in a CI variable, a Helm values file, or a .env. The platform re-mints the assertion on its own short cycle, so there is no refresh token either.
  • Admission is per subject, not per issuer. Trusting GitHub Actions as an issuer admits nothing on its own. An administrator admits specific subjects, such as repo:acme/payments-api:ref:refs/heads/main, and a token from any other repository or branch is rejected before any network call is made.
  • Sessions are visible and revocable. A workload session appears in the same sessions list as a human session, labeled by issuer and subject, and can be revoked individually. Revoking the agent principal revokes all of them.

Under Workload identities in an MCP server's auth configuration, adding an issuer is a matter of pasting its URL, confirming what discovery found, and picking a preset for the platform. A "test an assertion" tool accepts a sample token and reports accept or reject with the reason, which turns the most common failure (an audience mismatch) from a support ticket into a ten-second check.

Where is all of this enforced?

Identity and policy only matter if every call passes through a point that checks them. Speakeasy enforces at the MCP gateway: a hosted MCP endpoint under your organization's domain that serves the union of the MCP servers in a plugin. Claude Tag connects to that one endpoint rather than to Linear, Jira, and GitHub separately, and the same is true for Claude Code, Cursor, Codex, and any autonomous workload.

Every tool call through the gateway is authenticated as a principal, authorized against the intersection described above, and logged with the agent, operator, and authorizer attached. Because the gateway sits on the path rather than in a config file on someone's laptop, the checks cannot be bypassed by adding a server locally, and a policy change takes effect on the next call without redeploying anything.

The gateway is also where the Slack-specific data problem is solved. Permission to retrieve is different from permission to share. The CFO may be allowed to read every employee's salary, but reading it through Claude Tag in a channel leaks the result to every participant. Speakeasy addresses that in two ways.

  • DLP security policies. Security scanners run on every prompt, tool call, and assistant response at the gateway, for Claude Tag and every other agent. A policy that blocks sensitive data from appearing in a public assistant response is a few clicks to define and applies regardless of which client made the call.
  • Agent-scoped permissions. Because the call is resolved as an intersection, the CFO's own permissions are necessary but not sufficient. If the Claude Tag agent principal is not also permitted to call the compensation tool, the call is denied even for the CFO. The same person using the Rippling MCP server directly from their own session still has full access.

How does this map to Okta or another identity provider?

Nothing here replaces the identity provider (IdP). It extends it to cover actors the IdP was never designed to represent.

The diagram below shows what lives in the IdP, what lives in the control plane, and where the gateway enforces it.

Diagram showing an identity provider (human users, groups, and OIDC workload tokens) synced into the Speakeasy control plane, where an agent principal named agent:claude-tag is deliberately not synced from the IdP, and the MCP gateway enforcing policy on every call with the actor, operator, and authorizer recorded before reaching Linear, Jira, and GitHub.

  • Owners and operators are directory users. They come from Okta, Entra, or whichever IdP is connected over SAML or OIDC, with SCIM keeping membership current. When Okta deprovisions a user, the agents they owned are blocked until reassigned, and their operator attribution on historical calls is preserved.
  • Agents are deliberately not directory users. Creating an Okta user for each agent couples machine identity to human login, group propagation, and consent semantics that do not apply. An agent principal lives in the control plane, is owned by a directory user, and gets its permissions from policy rather than from group membership.
  • Roles and groups still drive policy. The roles Okta syncs into Speakeasy are the same roles that scope MCP server access and tool permissions for humans. An agent's policy is expressed in the same vocabulary, so a security team reviews one set of rules rather than two.
  • Okta can be a trusted workload issuer. For workloads that authenticate through Okta rather than a cloud platform, its OIDC issuer can be registered under Workload identities like any other, with claim matchers restricting which subjects are admitted.

Deploying Claude Tag with an identity of its own

Putting it together, an org-wide Claude Tag deployment looks like this.

  • Create an agent principal for Claude Tag under Agents, owned by whoever runs the Slack integration.
  • Attach policy to the agent: the MCP servers it can reach, the tools it may call, and the destructive tools it may not.
  • Issue the agent a credential with an expiry, approved by a named administrator, and connect the Slack app to the MCP gateway endpoint for the plugin that contains Linear, Jira, and whatever else it needs.
  • Turn on a DLP policy for public assistant responses.

From that point, every Slack user who mentions @claude gets the intersection of their own permissions and the agent's, every action lands in Linear attributed to the agent at that user's direction, and revoking the agent takes one click. The same agent principal, policy, and gateway serve an overnight triage job that authenticates with a GitHub Actions token instead of a Slack mention.

Frequently asked questions
What is Claude Tag?
Claude Tag is Claude in Slack. Mentioning @claude in a channel or thread invokes Claude with the thread as context, so it can summarize discussions, draft replies, and take actions in connected tools such as Linear through MCP servers.
Why not give Claude its own Linear account?
A dedicated account with an API key gives every Slack user the same permissions the account has, and it attributes every action to the account rather than to the person who asked. An agent principal fixes both: permissions are resolved per call as the intersection of the agent's policy and the invoking user's, and every action records the agent as actor and the user as operator.
What is the difference between an agent's owner and its operator?
The owner is the human who administers the agent: renaming, suspending, issuing credentials, and transferring it. Ownership confers no permissions in either direction. The operator is the human driving the agent on a specific call, recorded for attribution and used to bound what the call may do. An agent with no operator is running autonomously, which is a supported and normal case.
What is workload identity federation for MCP?
It lets an autonomous workload such as a CI job, Kubernetes pod, or agent runtime authenticate to an MCP server using the short-lived token its platform already issues, exchanged at the token endpoint with the RFC 7523 jwt-bearer grant. No long-lived secret is created, and the resulting session is bound to an agent principal, visible in the sessions list, and individually revocable.
Does this require Claude Enterprise?
No. The controls described here sit at the MCP gateway layer, between the agent and the systems it reaches, so they apply to Claude Tag, Claude Code, Cursor, Codex, and autonomous workloads regardless of plan tier.

Last updated on

AI everywhere.

Control here.