> ## Documentation Index
> Fetch the complete documentation index at: https://openfga.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Server Authorization

> Authorize Model Context Protocol (MCP) servers with OpenFGA. Per-tool, per-resource permission checks on every MCP request.

export const OpenFGACodeBlock = ({code, title}) => {
  const [ready, setReady] = useState(false);
  useEffect(() => {
    if (window.openfgaDsl) {
      setReady(true);
      return;
    }
    let script = document.querySelector('script[src="/openfga-dsl-highlight.js"]');
    const loaded = () => {
      if (window.openfgaDsl) setReady(true);
    };
    if (!script) {
      script = document.createElement('script');
      script.src = '/openfga-dsl-highlight.js';
    }
    script.addEventListener('load', loaded);
    if (!script.isConnected) document.head.appendChild(script);
    return () => script.removeEventListener('load', loaded);
  }, []);
  const trimmed = code.replace(/^\n/, '').replace(/\n$/, '');
  const tokens = ready && window.openfgaDsl ? window.openfgaDsl.tokenize(trimmed) : [{
    text: trimmed,
    type: 'default'
  }];
  const colors = ready && window.openfgaDsl ? window.openfgaDsl.colors : {};
  const themeStyle = {
    '--openfga-dark-background': colors.background,
    '--openfga-dark-default': colors.default,
    '--openfga-dark-accent': colors.green
  };
  return <div className="openfga-code-viewer" style={themeStyle}>
      {title && <div className="openfga-code-viewer__title">{title}</div>}
      <div className="openfga-code-viewer__code" data-language="dsl">
        {tokens.map((t, i) => <span className="openfga-code-token" data-token={t.type || 'default'} key={i} style={{
    '--openfga-dark-token-color': t.color || colors.default
  }}>
            {t.text}
          </span>)}
      </div>
    </div>;
};

A Model Context Protocol server exposes tools to a client. That makes it an authorization surface: every tool call should be checked against the calling user.

## The pattern

Model the MCP server's surface in OpenFGA:

* A `tool` type with a `can_call` relation.
* `user`, `group`, and `role` types so permissions can be granted directly, by group membership, or by role assignment.
* Relationship tuples that grant specific users — or everyone via `user:*` — access to specific tools.

On every MCP request:

1. Identify the calling user.
2. Call [check](/docs/interacting/relationship-queries) against the requested tool.
3. Allow or deny.

For listing tools to the client, use [list-objects](/docs/interacting/relationship-queries) so the client only ever sees what it could actually invoke.

## Simplified model

<OpenFGACodeBlock
  code={`model
\x20 schema 1.1

type user

type group
\x20 relations
\x20   define member: [user]

type role
\x20 relations
\x20   define assignee: [user, group#member]

type tool
\x20 relations
\x20   define can_call: [user:*, user, role#assignee]`}
/>

`can_call` accepts a public grant (`user:*`), a direct user, or any user assigned to a role — including users who are role assignees by way of group membership. The MCP server checks `tool:search#can_call` on every request, with the calling user as the subject.

## Going further

When modeling MCP servers, the caller is always an agent. If you want your API to have different permissions for specific agents, model the agent as its own principal so you can scope and revoke its access independently. See the [MCP authorization modeling guide](/docs/modeling/agents/mcp-authorization) for time-bounded grants via conditions, and [agents as principals](/docs/modeling/agents/agents-as-principals) for delegation patterns.

## Related reading

* [MCP authorization modeling guide](/docs/modeling/agents/mcp-authorization)
* [AI agent authorization](/docs/use-cases/ai-agent-authorization)
* [Authorization for AI Agents overview](/docs/modeling/agents)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.