I have built a few Model Context Protocol servers in public: for Jamf Pro, for Cisco Meraki, for SkuVault. They are useful and people use them. The one I run at work, for Okta, is the one I think about most, because the system behind it is identity itself. A tool that can read and change Okta can read and change who has access to everything. So the design question was never "can Claude call the Okta API." It was "what has to be true before I am comfortable letting it."
Who: the network decides before the server does
The server is self-hosted and only reachable over Tailscale. That sounds like a deployment detail but it is the first layer of authorization. Every request arrives with a Tailscale identity attached, which means the server knows which person's device is calling before it reads a byte of the request. There is no API key to leak, no public endpoint to scan, and a device that is not in the tailnet does not get a connection refused, it gets nothing at all.
Because our Tailscale identities come from Okta, this also means a person who has been offboarded loses access to the MCP server in the same moment they lose access to everything else. Nobody has to remember to rotate anything.
What: scopes are Okta groups, not a config file
Inside the server, each tool declares the scope it needs: read users, read groups, modify group membership, and so on. Which scopes a caller has is decided by their Okta group membership, looked up at request time. A helpdesk engineer can look up a user's status and group memberships. A senior engineer can also add someone to a group. Nobody can create an admin or change an authentication policy through this path at all, because no tool for that exists.
That last point matters more than the scoping. The safest capability is the one you did not build. The server exposes the narrow set of operations that IT does dozens of times a day and would benefit from doing in plain language. The rare, dangerous operations stay in Terraform, where they get a pull request and a reviewer.
If a tool would make you nervous in the hands of a new hire on their first day, it should not be a tool. Scoping limits the damage of a mistake. Not building the tool removes the mistake.
Who finds out: every call is a log line
Each tool invocation writes a structured record: who called, from which device, which tool, with which arguments, what Okta returned, and how long it took. Write operations also post a short summary to a Slack channel the whole IT team reads. That gives the same visibility as the just-in-time admin flow: changes made through the agent are not quieter than changes made by hand. They are louder.
The log was also the thing that made the rollout easy to defend. When security asked what the agent had done in its first month, the answer was a table, not a conversation.
The second user: a helpdesk agent that cannot write
Once the server existed, the obvious extension was to let an AI helpdesk agent use it. That agent drafts replies to tickets in Front. A lot of tickets are some version of "I can't get into X," and the right reply depends on facts that live in Okta: is the account active, is the device registered, are they in the right group.
The helpdesk agent gets its own identity with read-only scopes. It can look, it cannot touch. Its drafts go to a human before they go to the user. The result is that the first reply on an access ticket now usually contains the actual answer rather than a request for more information, and the engineer reviewing the draft can see the Okta facts that informed it.
What I would tell someone building one
- Decide who can connect at the network layer, with an identity you already trust, before you write any authorization code.
- Make scopes live in the identity provider so that access reviews cover the agent too.
- Build the small set of tools people use every day. Leave the dangerous operations in a path that has review.
- Log every call, and make writes visible to humans in the place they already look.
- Give automated callers their own identity with less access than the humans. An agent that only reads is an agent you can let run.