import type { Tool } from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
import { pocketBaseService, PocketBaseError } from '../pocketbase-service.js';
import { logger } from '../utils/logger.js';
import type {
ToolResult,
} from '../types/mcp.js';
import {
LoginParamsSchema,
RegisterParamsSchema,
RequestPasswordResetParamsSchema,
ConfirmPasswordResetParamsSchema,
} from '../types/mcp.js';
// Helper function to create tool results
function createResult<T = unknown>(
text: string,
isError = false,
meta?: T,
): ToolResult<T> {
const result: ToolResult<T> = {
content: [{ type: 'text', text }],
isError,
};
if (meta !== undefined) {
(result as any)._meta = meta;
}
return result;
}
// Helper function to validate parameters
function validateParams<T>(schema: z.ZodSchema<T>, params: unknown): T {
try {
return schema.parse(params);
} catch (error) {
if (error instanceof z.ZodError) {
const errors = error.errors.map((err) => `${err.path.join('.')}: ${err.message}`);
throw new Error(`Invalid parameters:\n${errors.join('\n')}`);
}
throw error;
}
}
// User login tool
export const loginTool: Tool = {
name: 'pb_auth_login',
description: 'Authenticate a user with email and password',
inputSchema: {
type: 'object',
properties: {
email: {
type: 'string',
format: 'email',
description: 'User email address',
},
password: {
type: 'string',
description: 'User password',
},
options: {
type: 'object',
properties: {
expand: {
type: 'string',
description: 'Relations to expand',
},
fields: {
type: 'string',
description: 'Fields to return',
},
},
},
},
required: ['email', 'password'],
},
};
export async function handleLogin(params: unknown): Promise<ToolResult> {
try {
const { email, password, options } = validateParams(LoginParamsSchema, params);
logger.info('Processing login request', { email });
const authOptions = options ? {
...(options.expand && { expand: options.expand }),
...(options.fields && { fields: options.fields }),
} : undefined;
const authData = await pocketBaseService.loginUser(email, password, authOptions);
const result = {
success: true,
user: {
id: authData.record.id,
email: authData.record.email,
username: authData.record.username,
verified: authData.record.verified,
created: authData.record.created,
updated: authData.record.updated,
},
token: authData.token,
};
return createResult(
`Login successful for user: ${email}`,
false,
result,
);
} catch (error) {
logger.error('Login failed', error);
if (error instanceof PocketBaseError) {
return createResult(
`Login failed: ${error.message}`,
true,
{ error: error.message, status: error.status },
);
}
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`Login failed: ${message}`, true);
}
}
// User registration tool
export const registerTool: Tool = {
name: 'pb_auth_register',
description: 'Register a new user account',
inputSchema: {
type: 'object',
properties: {
email: {
type: 'string',
format: 'email',
description: 'User email address',
},
password: {
type: 'string',
minLength: 8,
description: 'User password (minimum 8 characters)',
},
passwordConfirm: {
type: 'string',
minLength: 8,
description: 'Password confirmation',
},
username: {
type: 'string',
description: 'Optional username',
},
name: {
type: 'string',
description: 'Optional display name',
},
},
required: ['email', 'password', 'passwordConfirm'],
},
};
export async function handleRegister(params: unknown): Promise<ToolResult> {
try {
const {
email,
password,
passwordConfirm,
username,
name,
} = validateParams(RegisterParamsSchema, params);
if (password !== passwordConfirm) {
return createResult(
'Registration failed: Password confirmation does not match',
true,
);
}
logger.info('Processing registration request', { email, username });
const additionalData: Record<string, unknown> = {};
if (username) additionalData.username = username;
if (name) additionalData.name = name;
const authData = await pocketBaseService.registerUser(
email,
password,
passwordConfirm,
additionalData,
);
const result = {
success: true,
user: {
id: authData.record.id,
email: authData.record.email,
username: authData.record.username,
verified: authData.record.verified,
created: authData.record.created,
updated: authData.record.updated,
},
token: authData.token,
};
return createResult(
`Registration successful for user: ${email}`,
false,
result,
);
} catch (error) {
logger.error('Registration failed', error);
if (error instanceof PocketBaseError) {
return createResult(
`Registration failed: ${error.message}`,
true,
{ error: error.message, status: error.status },
);
}
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`Registration failed: ${message}`, true);
}
}
// Refresh authentication token tool
export const refreshAuthTool: Tool = {
name: 'pb_auth_refresh',
description: 'Refresh the current user authentication token',
inputSchema: {
type: 'object',
properties: {
expand: {
type: 'string',
description: 'Relations to expand',
},
fields: {
type: 'string',
description: 'Fields to return',
},
},
},
};
export async function handleRefreshAuth(params: unknown): Promise<ToolResult> {
try {
const options = params as { expand?: string; fields?: string };
logger.info('Processing auth refresh request');
const authData = await pocketBaseService.refreshUserAuth(options);
const result = {
success: true,
user: {
id: authData.record.id,
email: authData.record.email,
username: authData.record.username,
verified: authData.record.verified,
created: authData.record.created,
updated: authData.record.updated,
},
token: authData.token,
};
return createResult(
'Authentication token refreshed successfully',
false,
result,
);
} catch (error) {
logger.error('Auth refresh failed', error);
if (error instanceof PocketBaseError) {
return createResult(
`Auth refresh failed: ${error.message}`,
true,
{ error: error.message, status: error.status },
);
}
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`Auth refresh failed: ${message}`, true);
}
}
// Logout tool
export const logoutTool: Tool = {
name: 'pb_auth_logout',
description: 'Logout the current user and clear authentication',
inputSchema: {
type: 'object',
properties: {},
},
};
export async function handleLogout(): Promise<ToolResult> {
try {
logger.info('Processing logout request');
pocketBaseService.logoutUser();
return createResult(
'User logged out successfully',
false,
{ success: true },
);
} catch (error) {
logger.error('Logout failed', error);
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`Logout failed: ${message}`, true);
}
}
// Get current user tool
export const getCurrentUserTool: Tool = {
name: 'pb_auth_get_user',
description: 'Get information about the currently authenticated user',
inputSchema: {
type: 'object',
properties: {},
},
};
export async function handleGetCurrentUser(): Promise<ToolResult> {
try {
logger.info('Getting current user info');
const user = pocketBaseService.getCurrentUser();
const isAuthenticated = pocketBaseService.isUserAuthenticated();
if (!isAuthenticated || !user) {
return createResult(
'No user is currently authenticated',
false,
{ authenticated: false, user: null },
);
}
const result = {
authenticated: true,
user: {
id: user.id,
email: user.email,
username: user.username,
verified: user.verified,
emailVisibility: user.emailVisibility,
created: user.created,
updated: user.updated,
},
};
return createResult(
`Current user: ${user.email}`,
false,
result,
);
} catch (error) {
logger.error('Get current user failed', error);
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`Failed to get current user: ${message}`, true);
}
}
// Request password reset tool
export const requestPasswordResetTool: Tool = {
name: 'pb_auth_request_password_reset',
description: 'Request a password reset email for a user',
inputSchema: {
type: 'object',
properties: {
email: {
type: 'string',
format: 'email',
description: 'Email address to send password reset to',
},
},
required: ['email'],
},
};
export async function handleRequestPasswordReset(params: unknown): Promise<ToolResult> {
try {
const { email } = validateParams(RequestPasswordResetParamsSchema, params);
logger.info('Processing password reset request', { email });
const success = await pocketBaseService.requestPasswordReset(email);
if (success) {
return createResult(
`Password reset email sent to: ${email}`,
false,
{ success: true, email },
);
}
return createResult(
'Failed to send password reset email',
true,
);
} catch (error) {
logger.error('Password reset request failed', error);
if (error instanceof PocketBaseError) {
return createResult(
`Password reset request failed: ${error.message}`,
true,
{ error: error.message, status: error.status },
);
}
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`Password reset request failed: ${message}`, true);
}
}
// Confirm password reset tool
export const confirmPasswordResetTool: Tool = {
name: 'pb_auth_confirm_password_reset',
description: 'Confirm a password reset with the reset token',
inputSchema: {
type: 'object',
properties: {
token: {
type: 'string',
description: 'Password reset token from email',
},
password: {
type: 'string',
minLength: 8,
description: 'New password (minimum 8 characters)',
},
passwordConfirm: {
type: 'string',
minLength: 8,
description: 'New password confirmation',
},
},
required: ['token', 'password', 'passwordConfirm'],
},
};
export async function handleConfirmPasswordReset(params: unknown): Promise<ToolResult> {
try {
const { token, password, passwordConfirm } = validateParams(
ConfirmPasswordResetParamsSchema,
params,
);
if (password !== passwordConfirm) {
return createResult(
'Password reset failed: Password confirmation does not match',
true,
);
}
logger.info('Processing password reset confirmation');
const success = await pocketBaseService.confirmPasswordReset(
token,
password,
passwordConfirm,
);
if (success) {
return createResult(
'Password reset completed successfully',
false,
{ success: true },
);
}
return createResult(
'Failed to reset password',
true,
);
} catch (error) {
logger.error('Password reset confirmation failed', error);
if (error instanceof PocketBaseError) {
return createResult(
`Password reset confirmation failed: ${error.message}`,
true,
{ error: error.message, status: error.status },
);
}
const message = error instanceof Error ? error.message : 'Unknown error occurred';
return createResult(`Password reset confirmation failed: ${message}`, true);
}
}
// Export all auth tools and handlers
export const authTools = [
loginTool,
registerTool,
refreshAuthTool,
logoutTool,
getCurrentUserTool,
requestPasswordResetTool,
confirmPasswordResetTool,
];
export const authHandlers = {
pb_auth_login: handleLogin,
pb_auth_register: handleRegister,
pb_auth_refresh: handleRefreshAuth,
pb_auth_logout: handleLogout,
pb_auth_get_user: handleGetCurrentUser,
pb_auth_request_password_reset: handleRequestPasswordReset,
pb_auth_confirm_password_reset: handleConfirmPasswordReset,
};