audit.query
Query audit logs to view records of processing activities per GDPR Art.30. Filter by action, target type, and date range to monitor compliance.
Instructions
監査ログを検索します。GDPR Art.30に基づく処理活動記録の閲覧。Query audit logs. View records of processing activities per GDPR Art.30.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | No | アクションフィルタ(例: data.delete, page.analyze) / Action filter (e.g., data.delete, page.analyze) | |
| target_type | No | ターゲットタイプフィルタ(例: web_page, preference_profile) / Target type filter (e.g., web_page, preference_profile) | |
| start_date | No | 開始日時(ISO 8601形式) / Start date (ISO 8601 format) | |
| end_date | No | 終了日時(ISO 8601形式) / End date (ISO 8601 format) | |
| limit | No | 結果上限(最大100、デフォルト20) / Result limit (max 100, default 20) |
Implementation Reference
- Main handler function for audit.query tool. Validates input via Zod schema, checks DI factory for AuditLogService, calls service.query() with filters, maps results to output format, and returns success/failure.
export async function auditQueryHandler(input: unknown): Promise<AuditQueryOutput> { if (isDevelopment()) { logger.info("[MCP Tool] audit.query called"); } // 入力バリデーション / Input validation let validated: AuditQueryInput; try { validated = auditQueryInputSchema.parse(input); } catch (error) { if (error instanceof ZodError) { const errorMessage = error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", "); logger.warn("[MCP Tool] audit.query validation error", { errors: error.errors, }); return { success: false, error: { code: AUDIT_QUERY_ERROR_CODES.VALIDATION_ERROR, message: `Validation error: ${errorMessage}`, }, }; } throw error; } // サービスファクトリーチェック / Service factory check if (!auditLogServiceDI.get()) { logger.warn("[MCP Tool] audit.query service factory not set"); return { success: false, error: { code: AUDIT_QUERY_ERROR_CODES.SERVICE_UNAVAILABLE, message: "Audit log service is not available", }, }; } const service = auditLogServiceDI.get()!(); try { const records = await service.query({ action: validated.action, targetType: validated.target_type, startDate: validated.start_date ? new Date(validated.start_date) : undefined, endDate: validated.end_date ? new Date(validated.end_date) : undefined, limit: validated.limit, }); const logs = records.map(toOutputEntry); if (isDevelopment()) { logger.info("[MCP Tool] audit.query completed", { count: logs.length, }); } return { success: true, data: { logs, count: logs.length, }, }; } catch (error) { const errorInstance = error instanceof Error ? error : new Error(String(error)); logger.warn("[MCP Tool] audit.query error", { error: errorInstance.message, }); return { success: false, error: { code: AUDIT_QUERY_ERROR_CODES.INTERNAL_ERROR, message: sanitizeErrorMessage(error), }, }; } } - Input schema (Zod) for audit.query: optional action, target_type, start_date, end_date (ISO 8601 strings), and limit (1-100).
export const auditQueryInputSchema = z.object({ /** アクションフィルタ / Action filter */ action: z.string().max(100).optional(), /** ターゲットタイプフィルタ / Target type filter */ target_type: z.string().max(100).optional(), /** 開始日時(ISO 8601) / Start date (ISO 8601) */ start_date: isoDateStringSchema.optional(), /** 終了日時(ISO 8601) / End date (ISO 8601) */ end_date: isoDateStringSchema.optional(), /** 結果上限(最大100、デフォルト20) / Result limit (max 100, default 20) */ limit: z.number().int().min(1).max(100).optional(), }); - Output type for audit.query: success with data.logs[] and data.count, or failure with error.code and error.message.
export type AuditQueryOutput = | { success: true; data: { logs: AuditLogOutputEntry[]; count: number; }; } | { success: false; error: { code: string; message: string; }; }; - apps/mcp-server/src/tools/audit/query.tool.ts:251-296 (registration)Tool definition object with name 'audit.query', description, annotations (readOnly, idempotent), and JSON Schema inputSchema matching Zod schema (5 optional fields).
export const auditQueryToolDefinition = { name: "audit.query", description: "監査ログを検索します。GDPR Art.30に基づく処理活動記録の閲覧。" + "Query audit logs. View records of processing activities per GDPR Art.30.", annotations: { title: "Audit Log Query", readOnlyHint: true, idempotentHint: true, openWorldHint: false, }, inputSchema: { type: "object" as const, properties: { action: { type: "string", description: "アクションフィルタ(例: data.delete, page.analyze) / " + "Action filter (e.g., data.delete, page.analyze)", }, target_type: { type: "string", description: "ターゲットタイプフィルタ(例: web_page, preference_profile) / " + "Target type filter (e.g., web_page, preference_profile)", }, start_date: { type: "string", format: "date-time", description: "開始日時(ISO 8601形式) / Start date (ISO 8601 format)", }, end_date: { type: "string", format: "date-time", description: "終了日時(ISO 8601形式) / End date (ISO 8601 format)", }, limit: { type: "number", description: "結果上限(最大100、デフォルト20) / Result limit (max 100, default 20)", minimum: 1, maximum: 100, }, }, required: [], }, }; - apps/mcp-server/src/tools/index.ts:833-834 (registration)Registration of auditQueryHandler in the toolHandlers map under the key 'audit.query'.
// audit.query(監査ログ検索、GDPR Art.30) "audit.query": auditQueryHandler, - apps/mcp-server/src/tools/index.ts:701-702 (registration)auditQueryToolDefinition included in allToolDefinitions array for MCP server initialization.
// audit.query(監査ログ検索、GDPR Art.30) auditQueryToolDefinition, - Helper function toOutputEntry: converts DB AuditLogRecord to MCP output format (maps camelCase fields to snake_case).
function toOutputEntry(record: AuditLogRecord): AuditLogOutputEntry { return { id: record.id, timestamp: record.timestamp instanceof Date ? record.timestamp.toISOString() : String(record.timestamp), action: record.action, actor: record.actor, target_type: record.targetType, target_id: record.targetId, details: record.details, result: record.result, }; } - Helper function truncateTargetId: truncates PII-sensitive target IDs for privacy (GDPR compliance).
function truncateTargetId(id: string | undefined | null): string | null { if (!id) return null; if (id.length <= AUDIT_LOG_CONSTANTS.TARGET_ID_TRUNCATE_LENGTH) return id; return id.slice(0, AUDIT_LOG_CONSTANTS.TARGET_ID_TRUNCATE_LENGTH) + "..."; } - Helper function sanitizeDetails: recursively removes sensitive keys (password, secret, token, etc.) from details objects.
function sanitizeDetails( obj: Record<string, unknown> | null | undefined ): Record<string, unknown> | null { if (!obj || typeof obj !== "object") return null; const sanitized: Record<string, unknown> = {}; for (const [key, value] of Object.entries(obj)) { // 機密キーはスキップ if (SENSITIVE_KEYS.has(key)) { continue; } // ネストされたオブジェクトは再帰処理 if (value !== null && typeof value === "object" && !Array.isArray(value)) { sanitized[key] = sanitizeDetails(value as Record<string, unknown>); } else { sanitized[key] = value; } } return sanitized; } - AuditLogService.query() method: builds Prisma where clause from filters, applies limit clamping (1-100), orders by timestamp desc, and returns records.
async query(filters: AuditLogFilters): Promise<AuditLogRecord[]> { if (!this.prismaClient) { logger.warn("[AuditLog] Prisma client not available for query"); return []; } const where: Record<string, unknown> = {}; if (filters.action) { where.action = filters.action; } if (filters.targetType) { where.targetType = filters.targetType; } if (filters.startDate || filters.endDate) { const timestampFilter: Record<string, Date> = {}; if (filters.startDate) { timestampFilter.gte = filters.startDate; } if (filters.endDate) { timestampFilter.lte = filters.endDate; } where.timestamp = timestampFilter; } const limit = Math.min( Math.max(filters.limit ?? AUDIT_LOG_CONSTANTS.DEFAULT_QUERY_LIMIT, 1), AUDIT_LOG_CONSTANTS.MAX_QUERY_LIMIT ); return this.prismaClient.auditLog.findMany({ where, orderBy: { timestamp: "desc" }, take: limit, }); } - apps/mcp-server/src/middleware/auth.ts:262-263 (registration)Auth permission mapping: audit.query requires SYSTEM_READ permission.
// 監査系(v0.3.0 GDPR Art.30) "audit.query": [PERMISSIONS.SYSTEM_READ], - DI factory registration: setAuditQueryServiceFactory wires AuditLogService into the audit.query tool handler at service initialization.
// audit.query ツールの DI ファクトリ登録 setAuditQueryServiceFactory(() => getAuditLogService()); result.registeredFactories.push("auditLogPrisma", "auditQueryService"); result.categories.push("auditLog"); logger.info("[ServiceInitializer] auditLog factories registered (prisma, queryService)"); } catch (error) { recordInitError(result, "AuditLog", ["auditLogPrisma", "auditQueryService"], error); }