Skip to main content
Glama
kdunin

Logto MCP Server

by kdunin
README.md
# Logto MCP Server

A [Model Context Protocol](https://modelcontextprotocol.io/) server that exposes the [Logto Management API](https://docs.logto.io/integrate-logto/interact-with-management-api) to MCP clients (Cursor, Claude Desktop, etc.).

Built with:

- [`@modelcontextprotocol/sdk`](https://modelcontextprotocol.io/docs/develop/build-server#typescript) (stdio transport)
- [`@logto/api`](https://www.npmjs.com/package/@logto/api) Management SDK

## Prerequisites

1. A Logto instance (self-hosted OSS or Cloud)
2. A **Machine-to-machine** application in Console → Applications
3. An M2M role that includes **Logto Management API** permissions (`all`)

See [Interact with Management API](https://docs.logto.io/integrate-logto/interact-with-management-api) for details.

## Setup

```bash
npm install
npm run build
```

## Environment variables

| Variable | Required | Description |
|----------|----------|-------------|
| `LOGTO_APP_ID` / `LOGTO_CLIENT_ID` | Yes | M2M application ID |
| `LOGTO_APP_SECRET` / `LOGTO_CLIENT_SECRET` | Yes | M2M application secret |
| `LOGTO_ENDPOINT` / `LOGTO_BASE_URL` | OSS | Self-hosted Logto base URL |
| `LOGTO_TENANT_ID` | Cloud | Cloud tenant ID (OSS defaults to `default`) |
| `LOGTO_API_INDICATOR` | No | OSS default: `https://default.logto.app/api` |

Copy `.env.example` as a reference. For MCP clients, pass variables in the `env` block (below).

### Self-hosted / OSS

```env
LOGTO_ENDPOINT=https://your.logto.endpoint
LOGTO_APP_ID=...
LOGTO_APP_SECRET=...
# Optional: LOGTO_TENANT_ID=default
# Optional: LOGTO_API_INDICATOR=https://default.logto.app/api
```

### Logto Cloud

```env
LOGTO_TENANT_ID=your-tenant-id
LOGTO_APP_ID=...
LOGTO_APP_SECRET=...
```

## Cursor / Claude Desktop / MetaMCP config

Local build:

```json
{
  "mcpServers": {
    "logto": {
      "command": "node",
      "args": ["C:/Users/rascal/projects/logto-mcp/build/index.js"],
      "env": {
        "LOGTO_ENDPOINT": "https://your.logto.endpoint",
        "LOGTO_APP_ID": "your-m2m-app-id",
        "LOGTO_APP_SECRET": "your-m2m-app-secret"
      }
    }
  }
}
```

From GitHub repo (no package registry auth):

```json
{
  "mcpServers": {
    "logto": {
      "command": "npx",
      "args": ["-y", "github:KDunin/logto-mcp"],
      "env": {
        "LOGTO_ENDPOINT": "https://your.logto.endpoint",
        "LOGTO_APP_ID": "your-m2m-app-id",
        "LOGTO_APP_SECRET": "your-m2m-app-secret"
      }
    }
  }
}
```

From GitHub Packages (after a release is published):

```bash
# once in ~/.npmrc (needs a PAT with read:packages)
echo "@kdunin:registry=https://npm.pkg.github.com" >> ~/.npmrc
echo "//npm.pkg.github.com/:_authToken=YOUR_GH_PAT" >> ~/.npmrc
```

```json
{
  "mcpServers": {
    "logto": {
      "command": "npx",
      "args": ["-y", "@kdunin/logto-mcp"],
      "env": {
        "LOGTO_ENDPOINT": "https://your.logto.endpoint",
        "LOGTO_APP_ID": "your-m2m-app-id",
        "LOGTO_APP_SECRET": "your-m2m-app-secret"
      }
    }
  }
}
```

Use an absolute path to `build/index.js` for local runs. After changing env or rebuilding, restart the MCP client.

## Tools

| Tool | Description |
|------|-------------|
| `logto_health` | Verify M2M credentials |
| `list_users` / `get_user` / `create_user` / `update_user` / `delete_user` | Users |
| `list_applications` / `get_application` / `create_application` / `update_application` / `delete_application` | Applications |
| `list_organizations` / `get_organization` / `create_organization` / `update_organization` / `delete_organization` | Organizations |
| `list_organization_members` / `add_organization_members` / `remove_organization_member` | Organization members |
| `list_roles` / `get_role` / `create_role` / `update_role` / `delete_role` | Roles |
| `list_user_roles` / `assign_user_roles` | User ↔ role assignment |

## Development

```bash
npm run build   # compile TypeScript to build/
npm start       # run stdio server (expects MCP client on stdin)
npm run dev     # tsc --watch
```

Logging goes to **stderr** only so stdout stays reserved for MCP JSON-RPC.

## Releasing

Releases are automated by [`.github/workflows/release.yml`](.github/workflows/release.yml) when you push a version tag.

1. Bump `version` in `package.json` (must match the tag without the `v` prefix).
2. Commit, then tag and push:

```bash
git tag v1.0.1
git push origin v1.0.1
```

The workflow will:

- Build the package
- Create a GitHub Release with notes and the `.tgz` artifact
- Publish to **GitHub Packages** as `@kdunin/logto-mcp` (stable tags only; uses `GITHUB_TOKEN`, no npmjs login)

`ENEEDAUTH` against `registry.npmjs.org` means you were targeting the public npm registry. This project publishes to `https://npm.pkg.github.com` instead.

Prerelease tags like `v1.0.1-beta.1` create a GitHub prerelease and skip package publish.

## License

MIT

TDQS

B3.3/5.0

Scored across 26 tools

Disambiguation5/5

Every tool targets a distinct resource-action pair (user, application, organization, role) with no ambiguity. Even similar operations like list_users vs list_organization_members are clearly differentiated by resource.

Naming Consistency4/5

Tools consistently use verb_noun pattern with snake_case (e.g., get_user, create_user). The only deviation is the logto_health tool which doesn't follow the pattern, but it's a single utility tool.

Tool Count4/5

26 tools is slightly above the typical 3-15 range but each tool serves a necessary CRUD or management function across four resource types. The count is justified for the scope.

Completeness4/5

CRUD operations are fully covered for users, applications, organizations, and roles. Organization membership and user role assignment are also included. Missing details like application secrets or OIDC config are minor gaps.

Maintenance

ActivitySlowing
ResponsivenessNo issues