/**
* mysql-mcp - OAuth Scopes
*
* Scope definitions and utilities for MySQL MCP OAuth 2.0.
*/
import type { ToolGroup } from "../types/index.js";
// =============================================================================
// Scope Definitions
// =============================================================================
/**
* Standard OAuth scopes for mysql-mcp
*/
export const SCOPES = {
/** Read-only access to all databases */
READ: "read",
/** Read and write access to all databases */
WRITE: "write",
/** Administrative access (OPTIMIZE, ANALYZE, etc.) */
ADMIN: "admin",
/** Full access to all operations */
FULL: "full",
} as const;
export type StandardScope = (typeof SCOPES)[keyof typeof SCOPES];
/**
* All supported scopes including patterns
*/
export const ALL_SCOPES = [
SCOPES.READ,
SCOPES.WRITE,
SCOPES.ADMIN,
SCOPES.FULL,
// Pattern scopes: db:{name}, table:{db}:{table}
] as const;
// =============================================================================
// Tool Group to Scope Mapping
// =============================================================================
/**
* Map tool groups to required minimum scopes
*/
export const TOOL_GROUP_SCOPES: Record<ToolGroup, StandardScope> = {
core: SCOPES.READ,
json: SCOPES.READ,
text: SCOPES.READ,
fulltext: SCOPES.READ,
performance: SCOPES.READ,
optimization: SCOPES.READ,
monitoring: SCOPES.READ,
admin: SCOPES.ADMIN,
backup: SCOPES.ADMIN,
replication: SCOPES.ADMIN,
partitioning: SCOPES.ADMIN,
transactions: SCOPES.WRITE,
router: SCOPES.READ,
proxysql: SCOPES.READ,
shell: SCOPES.ADMIN,
// New tool groups (v2.0.0)
schema: SCOPES.READ,
events: SCOPES.ADMIN,
sysschema: SCOPES.READ,
stats: SCOPES.READ,
spatial: SCOPES.READ,
security: SCOPES.ADMIN,
cluster: SCOPES.READ,
roles: SCOPES.ADMIN,
docstore: SCOPES.WRITE,
};
// =============================================================================
// Scope Utilities
// =============================================================================
/**
* Parse scope string into array
*/
export function parseScopes(scopeString: string | undefined): string[] {
if (!scopeString) return [];
return scopeString.split(" ").filter((s) => s.length > 0);
}
/**
* Check if granted scopes include the required scope
*/
export function hasScope(
grantedScopes: string[],
requiredScope: string,
): boolean {
// Full scope grants everything
if (grantedScopes.includes(SCOPES.FULL)) {
return true;
}
// Direct match
if (grantedScopes.includes(requiredScope)) {
return true;
}
// Admin scope includes write and read
if (requiredScope === SCOPES.READ || requiredScope === SCOPES.WRITE) {
if (grantedScopes.includes(SCOPES.ADMIN)) {
return true;
}
}
// Write scope includes read
if (requiredScope === SCOPES.READ) {
if (grantedScopes.includes(SCOPES.WRITE)) {
return true;
}
}
return false;
}
/**
* Check if granted scopes include any of the required scopes
*/
export function hasAnyScope(
grantedScopes: string[],
requiredScopes: string[],
): boolean {
return requiredScopes.some((scope) => hasScope(grantedScopes, scope));
}
/**
* Check if granted scopes include all of the required scopes
*/
export function hasAllScopes(
grantedScopes: string[],
requiredScopes: string[],
): boolean {
return requiredScopes.every((scope) => hasScope(grantedScopes, scope));
}
/**
* Get the required scope for a tool group
*/
export function getScopeForToolGroup(group: ToolGroup): StandardScope {
return TOOL_GROUP_SCOPES[group] ?? SCOPES.READ;
}
/**
* Check if database-specific scope matches
*/
export function hasDatabaseScope(
grantedScopes: string[],
database: string,
): boolean {
// Full or admin grants all databases
if (
grantedScopes.includes(SCOPES.FULL) ||
grantedScopes.includes(SCOPES.ADMIN)
) {
return true;
}
// Check for db:{name} pattern
const dbScope = `db:${database}`;
return grantedScopes.includes(dbScope);
}
/**
* Check if table-specific scope matches
*/
export function hasTableScope(
grantedScopes: string[],
database: string,
table: string,
): boolean {
// Full or admin grants all tables
if (
grantedScopes.includes(SCOPES.FULL) ||
grantedScopes.includes(SCOPES.ADMIN)
) {
return true;
}
// Check for db:{name} scope (grants access to all tables in db)
if (hasDatabaseScope(grantedScopes, database)) {
return true;
}
// Check for table:{db}:{table} pattern
const tableScope = `table:${database}:${table}`;
return grantedScopes.includes(tableScope);
}
/**
* Get scope display name
*/
export function getScopeDisplayName(scope: string): string {
switch (scope) {
case SCOPES.READ:
return "Read Only";
case SCOPES.WRITE:
return "Read/Write";
case SCOPES.ADMIN:
return "Administrative";
case SCOPES.FULL:
return "Full Access";
default:
if (scope.startsWith("db:")) {
return `Database: ${scope.slice(3)}`;
}
if (scope.startsWith("table:")) {
return `Table: ${scope.slice(6)}`;
}
return scope;
}
}