Skip to content

chore: fix lint error #42

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions docs/configure-server/mcp-auth.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ If your issuer includes a path, the behavior differs slightly between OAuth 2.0
- **OAuth 2.0**: The well-known URL is appended to the **domain** of the issuer. For example, if your issuer is `https://my-project.logto.app/oauth`, the well-known URL will be `https://auth.logto.io/.well-known/oauth-authorization-server/oauth`.
- **OpenID Connect**: The well-known URL is appended directly to the **issuer**. For example, if your issuer is `https://my-project.logto.app/oidc`, the well-known URL will be `https://auth.logto.io/oidc/.well-known/openid-configuration`.

### Other ways to initialize MCP Auth {#other-ways}
### Other ways to initialize MCP Auth \{#other-ways}

#### Custom data transpilation \{#custom-data-transpilation}

Expand All @@ -76,6 +76,7 @@ mcp_auth = MCPAuth(
)
)
```

</TabItem>
<TabItem value="node" label="Node.js">

Expand All @@ -88,6 +89,7 @@ const mcpAuth = new MCPAuth({
}),
});
```

</TabItem>
</Tabs>

Expand All @@ -107,7 +109,7 @@ from mcpauth.utils import fetch_server_config_by_well_known_url

mcp_auth = MCPAuth(
server=fetch_server_config_by_well_known_url(
'<metadata-url>',
'<metadata-url>',
type=AuthServerType.OIDC # or AuthServerType.OAUTH
)
)
Expand Down Expand Up @@ -182,7 +184,7 @@ mcp_auth = MCPAuth(
# ... other metadata fields
),
)
)
)
```

</TabItem>
Expand Down
4 changes: 2 additions & 2 deletions docs/provider-list.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ If you have tested MCP Auth with another provider, please feel free to submit a

[^4]: Auth0 supports multi-resource refresh tokens (MRRT) but not full RFC 8707. Resource indicator support is limited and not standards-based.

## Is Dynamic Client Registration required? {#is-dcr-required}
## Is Dynamic Client Registration required? \{#is-dcr-required}

[Dynamic Client Registration](https://datatracker.ietf.org/doc/html/rfc7591) is not required for MCP servers and MCP Auth. In fact, you can choose the approach that best suits your needs:

Expand All @@ -37,7 +37,7 @@ If you have tested MCP Auth with another provider, please feel free to submit a
2. Alternatively, you can develop a custom registration flow that allows your MCP clients to register with the provider using a secure and controlled process, such as a web interface or an API endpoint that you control, without relying on Dynamic Client Registration.
As long as your provider supports Management API or similar functionality, you can use it in your custom endpoints to register the MCP clients.

## Test your provider {#test-your-provider}
## Test your provider \{#test-your-provider}

Enter the URL of your authorization server's `issuer` or metadata endpoint below to check if it's compatible with MCP.

Expand Down
102 changes: 52 additions & 50 deletions docs/tutorials/todo-manager/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ To implement [role-based access control (RBAC)](https://auth.wiki/rbac) in your
1. Sign in to [Logto Console](https://cloud.logto.io) (or your self-hosted Logto Console)

2. Create API resource and scopes:

- Go to "API Resources"
- Create a new API resource named "Todo Manager"
- Add the following scopes:
Expand All @@ -73,6 +74,7 @@ To implement [role-based access control (RBAC)](https://auth.wiki/rbac) in your
- `delete:todos`: "Delete any todo item"

3. Create roles (recommended for easier management):

- Go to "Roles"
- Create an "Admin" role and assign all scopes (`create:todos`, `read:todos`, `delete:todos`)
- Create a "User" role and assign only the `create:todos` scope
Expand All @@ -97,6 +99,7 @@ OAuth 2.0 / OIDC providers typically support scope-based access control. When im
4. The scopes are usually included in the JWT access token's `scope` claim

Check your provider's documentation for specific details on:

- How to define and manage scopes
- How scopes are included in the access token
- Any additional RBAC features like role management
Expand All @@ -121,7 +124,7 @@ sequenceDiagram
participant Auth Server

Client->>MCP Server: Request with access token

alt JWT Validation
MCP Server->>Auth Server: Fetch JWKS
Auth Server-->>MCP Server: Return JWKS
Expand All @@ -130,9 +133,9 @@ sequenceDiagram
MCP Server->>Auth Server: POST /introspect<br/>(token=access_token)
Auth Server-->>MCP Server: Return token info<br/>(active, scope, etc.)
end

MCP Server->>MCP Server: Extract & check scopes

alt Has required scopes
MCP Server->>Client: Allow operation
else Missing scopes
Expand Down Expand Up @@ -171,8 +174,8 @@ To control access to these tools, we define the following scopes:
We'll define two roles with different levels of access:

| Role | create:todos | read:todos | delete:todos |
|-------|-------------|------------|--------------|
| Admin | ✅ | ✅ | ✅ |
| ----- | ------------ | ---------- | ------------ |
| Admin | ✅ | ✅ | ✅ |
| User | ✅ | | |

- **User**: A regular user who can create todo items and view or delete only their own todos
Expand All @@ -185,7 +188,6 @@ While the permission table above shows the explicit scopes assigned to each role
- **Users** don't have the `read:todos` or `delete:todos` scopes, but they can still:
- Read their own todo items
- Delete their own todo items

- **Admins** have full permissions (`read:todos` and `delete:todos`), allowing them to:
- View all todo items in the system
- Delete any todo item, regardless of ownership
Expand All @@ -208,6 +210,7 @@ To implement the access control system we described earlier, you'll need to conf
1. Sign in to [Logto Console](https://cloud.logto.io) (or your self-hosted Logto Console)

2. Create API resource and scopes:

- Go to "API Resources"
- Create a new API resource named "Todo Manager" and using `https://todo.mcp-server.app` (demo purpose) as the indicator.
- Create the following scopes:
Expand All @@ -216,6 +219,7 @@ To implement the access control system we described earlier, you'll need to conf
- `delete:todos`: "Delete any todo item"

3. Create roles (recommended for easier management):

- Go to "Roles"
- Create an "Admin" role and assign all scopes (`create:todos`, `read:todos`, `delete:todos`)
- Create a "User" role and assign only the `create:todos` scope
Expand All @@ -241,13 +245,15 @@ When requesting an access token, Logto will include scopes in the token's `scope
In [Keycloak](https://www.keycloak.org), you can set up the required permissions using client scopes:

1. Create client scopes:

- In your realm, go to "Client scopes"
- Create three new client scopes:
- `create:todos`
- `read:todos`
- `delete:todos`

2. Configure the client:

- Go to your client settings
- In the "Client scopes" tab, add all the scopes you created
- Make sure the token mapper is configured to include scopes
Expand All @@ -267,12 +273,14 @@ Keycloak will include the granted scopes in the access token's `scope` claim.
For OAuth 2.0 or OpenID Connect providers, you'll need to configure the scopes that represent different permissions. The exact steps will depend on your provider, but generally:

1. Define scopes:

- Configure your authorization server to support:
- `create:todos`
- `read:todos`
- `delete:todos`

2. Configure client:

- Register or update your client to request these scopes
- Ensure the scopes are included in the access token

Expand All @@ -289,6 +297,7 @@ Most providers will include the granted scopes in the access token's `scope` cla
</Tabs>

After configuring your authorization server, users will receive access tokens containing their granted scopes. The MCP server will use these scopes to determine:

- Whether a user can create new todos (`create:todos`)
- Whether a user can view all todos (`read:todos`) or only their own
- Whether a user can delete any todo (`delete:todos`) or only their own
Expand Down Expand Up @@ -420,37 +429,23 @@ const server = new McpServer({
version: '0.0.0',
});

server.tool(
'create-todo',
'Create a new todo',
{ content: z.string() },
async ({ content }) => {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'Not implemented' }) }],
};
}
);
server.tool('create-todo', 'Create a new todo', { content: z.string() }, async ({ content }) => {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'Not implemented' }) }],
};
});

server.tool(
'get-todos',
'List all todos',
async () => {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'Not implemented' }) }],
};
}
);
server.tool('get-todos', 'List all todos', async () => {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'Not implemented' }) }],
};
});

server.tool(
'delete-todo',
'Delete a todo by id',
{ id: z.string() },
async ({ id }) => {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'Not implemented' }) }],
};
}
);
server.tool('delete-todo', 'Delete a todo by id', { id: z.string() }, async ({ id }) => {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'Not implemented' }) }],
};
});

// Below is the boilerplate code from MCP SDK documentation
const PORT = 3001;
Expand Down Expand Up @@ -562,6 +557,7 @@ This is usually the base URL of your authorization server, such as `https://auth
When requesting access tokens from different authorization servers, you'll encounter various approaches for specifying the target resource and permissions. Here are the main patterns:

- **Resource indicator based**:

- Uses the `resource` parameter to specify the target API (see [RFC 8707: Resource Indicators for OAuth 2.0](https://datatracker.ietf.org/doc/html/rfc8707))
- Common in modern OAuth 2.0 implementations
- Example request:
Expand All @@ -574,6 +570,7 @@ When requesting access tokens from different authorization servers, you'll encou
- The server issues tokens specifically bound to the requested resource

- **Audience based**:

- Uses the `audience` parameter to specify the intended token recipient
- Similar to resource indicators but with different semantics
- Example request:
Expand All @@ -597,11 +594,12 @@ When requesting access tokens from different authorization servers, you'll encou
- Common in simpler OAuth 2.0 implementations

:::tip Best Practices

- Check your provider's documentation for supported parameters
- Some providers support multiple approaches simultaneously
- Resource indicators provide better security through audience restriction
- Consider using resource indicators when available for better access control
:::
:::

</details>

Expand Down Expand Up @@ -820,17 +818,17 @@ import string

@dataclass
class Todo:
id: str
content: str
owner_id: str
created_at: str
id: str
content: str
owner_id: str
created_at: str

class TodoService:
"""A simple Todo service for demonstration purposes.
Use an in-memory array to store todos
"""
def __init__(self):
self.todos: list[Todo] = []
"""A simple Todo service for demonstration purposes.
Use an in-memory array to store todos
"""
def **init**(self):
self.todos: list[Todo] = []

def get_all_todos(self, owner_id: Optional[str] = None) -> list[Todo]:
if owner_id:
Expand Down Expand Up @@ -859,7 +857,8 @@ class TodoService:
def _gen_id(self) -> str:
"""Generate a random 8-character string for todo ID."""
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))
```

````


</TabItem>
Expand Down Expand Up @@ -922,7 +921,8 @@ export class TodoService {
return Math.random().toString(36).slice(2, 10);
}
}
```
````

</TabItem>
</Tabs>

Expand Down Expand Up @@ -954,12 +954,12 @@ todo_service = TodoService()
@mcp.tool()
def create_todo(content: str) -> dict[str, Any]:
"""Create a new todo.

Only users with 'create:todos' scope can create todos.
"""
# Get authentication info
auth_info = mcp_auth.auth_info

# Validate user ID
try:
user_id = assert_user_id(auth_info)
Expand All @@ -972,7 +972,7 @@ def create_todo(content: str) -> dict[str, Any]:

# Create new todo
created_todo = todo_service.create_todo(content=content, owner_id=user_id)

# Return the created todo
return created_todo.__dict__

Expand Down Expand Up @@ -1044,6 +1044,7 @@ Restart your MCP server and open the MCP inspector in your browser. When you cli
Once you sign in and back to the MCP inspector, repeat the actions we did in the previous checkpoint to run todo manager tools. This time, you can use these tools with your authenticated user identity. The behavior of the tools will depend on the roles and permissions assigned to your user:

- If you're logged in as a **User** (with only `create:todos` scope):

- You can create new todos using the `create-todo` tool
- You can only view and delete your own todos
- You won't be able to see or delete other users' todos
Expand All @@ -1054,6 +1055,7 @@ Once you sign in and back to the MCP inspector, repeat the actions we did in the
- You can delete any todo using the `delete-todo` tool, regardless of who created it

You can test these different permission levels by:

1. Signing out of the current session (click the "Disconnect" button in MCP inspector)
2. Signing in with a different user account that has different roles/permissions
3. Trying the same tools again to observe how the behavior changes based on the user's permissions
Expand Down
4 changes: 4 additions & 0 deletions docs/tutorials/whoami/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -362,15 +362,18 @@ docker run -p 8080:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADM
```

2. Access the Keycloak Admin Console (http://localhost:8080/admin) and log in with these credentials:

- Username: `admin`
- Password: `admin`

3. Create a new Realm:

- Click "Create Realm" in the top-left corner
- Enter `mcp-realm` in the "Realm name" field
- Click "Create"

4. Create a test user:

- Click "Users" in the left menu
- Click "Create new user"
- Fill in the user details:
Expand All @@ -380,6 +383,7 @@ docker run -p 8080:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADM
- In the "Credentials" tab, set a password and uncheck "Temporary"

5. Register MCP Inspector as a client:

- Open your MCP inspector, click on the "OAuth Configuration" button. Copy the **Redirect URL (auto-populated)** value, which should be something like `http://localhost:6274/oauth/callback`.
- In the Keycloak Admin Console, click "Clients" in the left menu
- Click "Create client"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"write-translations": "docusaurus write-translations",
"write-heading-ids": "node write-heading-ids.mjs",
"typecheck": "tsc",
"lint": "eslint src docs references"
"lint": "eslint src docs"
},
"dependencies": {
"@docusaurus/core": "3.7.0",
Expand Down