@shirokuma-library/mcp-knowledge-base
Version:
Shirokuma MCP Server for comprehensive knowledge management including issues, plans, documents, and work sessions. All stored data is structured for AI processing, not human readability.
175 lines (174 loc) • 7.26 kB
JavaScript
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
import { TypeRepository } from '../database/type-repository.js';
import { CreateTypeSchema, GetTypesSchema, DeleteTypeSchema, UpdateTypeSchema } from '../schemas/type-schemas.js';
import { createLogger } from '../utils/logger.js';
export class TypeHandlers {
db;
typeRepo;
logger = createLogger('TypeHandlers');
handlerName = 'TypeHandlers';
constructor(db) {
this.db = db;
this.typeRepo = new TypeRepository(db);
}
async init() {
await this.typeRepo.init();
}
async handleCreateType(args) {
try {
const validatedArgs = CreateTypeSchema.parse(args);
await this.typeRepo.createType(validatedArgs.name, validatedArgs.base_type, validatedArgs.description);
return {
content: [
{
type: 'text',
text: `Type "${validatedArgs.name}" created successfully with base_type "${validatedArgs.base_type}"`
}
]
};
}
catch (error) {
if (error instanceof Error) {
throw new McpError(ErrorCode.InvalidRequest, error.message);
}
throw error;
}
}
async handleGetTypes(args) {
const validatedArgs = GetTypesSchema.parse(args);
const types = await this.typeRepo.getAllTypes();
const typesByBase = {};
for (const type of types) {
if (!typesByBase[type.base_type]) {
typesByBase[type.base_type] = [];
}
typesByBase[type.base_type].push(type);
}
let output = '## Available Types\n\n';
if (typesByBase['tasks']) {
output += '### Tasks (Task Management)\n';
output += 'Task types with status and priority. Used for project management and bug tracking.\n\n';
output += '| Type | Description |\n';
output += '|------|-------------|\n';
for (const type of typesByBase['tasks']) {
const desc = type.description || 'Custom Task Type';
output += `| ${type.type} | ${desc} |\n`;
}
output += '\n';
}
if (typesByBase['documents']) {
output += '### Documents (Documents)\n';
output += 'Document types with required content. Used for knowledge base and technical documentation.\n\n';
output += '| Type | Description |\n';
output += '|------|-------------|\n';
for (const type of typesByBase['documents']) {
const desc = type.description || 'Custom Document Type';
output += `| ${type.type} | ${desc} |\n`;
}
output += '\n';
}
for (const [baseType, typeList] of Object.entries(typesByBase)) {
if (baseType !== 'tasks' && baseType !== 'documents') {
output += `### ${baseType}\n\n`;
output += '| Type | Base Type |\n';
output += '|------|-----------|\n';
for (const type of typeList) {
output += `| ${type.type} | ${type.base_type} |\n`;
}
output += '\n';
}
}
output += '### Special Types\n';
output += 'These types have special ID formats and are always available:\n\n';
output += '| Type | Description | ID Format |\n';
output += '|------|-------------|----------|\n';
output += '| sessions | Work session tracking. Content is optional - can be created at session start and updated later. | YYYY-MM-DD-HH.MM.SS.sss |\n';
output += '| dailies | Daily summaries with required content. One entry per date. | YYYY-MM-DD |\n';
output += '\n';
if (validatedArgs.include_definitions) {
output += '## Type Definitions (JSON)\n\n';
output += '```json\n';
const definitions = [
...types.map(t => ({
type: t.type,
base_type: t.base_type,
description: t.description,
supported_fields: this.getFieldsForBaseType(t.base_type)
})),
{
type: 'sessions',
base_type: 'sessions',
description: 'Work session tracking. Content is optional - can be created at session start and updated later. Uses timestamp-based IDs.',
supported_fields: ['title', 'content', 'description', 'tags', 'related_tasks', 'related_documents', 'version']
},
{
type: 'dailies',
base_type: 'documents',
description: 'Daily summaries with required content. One entry per date. Uses date as ID (YYYY-MM-DD).',
supported_fields: ['title', 'content', 'description', 'tags', 'related_tasks', 'related_documents', 'version']
}
];
output += JSON.stringify(definitions);
output += '\n```\n';
}
return {
content: [
{
type: 'text',
text: output
}
]
};
}
getFieldsForBaseType(baseType) {
switch (baseType) {
case 'tasks':
return ['title', 'content', 'description', 'priority', 'status', 'tags', 'start_date', 'end_date', 'related_tasks', 'version'];
case 'documents':
return ['title', 'content', 'description', 'tags', 'version'];
default:
return ['title', 'content', 'tags', 'version'];
}
}
async handleUpdateType(args) {
try {
const validatedArgs = UpdateTypeSchema.parse(args);
await this.typeRepo.updateType(validatedArgs.name, validatedArgs.description);
return {
content: [
{
type: 'text',
text: `Type "${validatedArgs.name}" description updated successfully`
}
]
};
}
catch (error) {
this.logger.error('Failed to update type', { error, args });
throw new McpError(ErrorCode.InternalError, `Failed to update type: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async handleDeleteType(args) {
try {
const validatedArgs = DeleteTypeSchema.parse(args);
await this.typeRepo.deleteType(validatedArgs.name);
return {
content: [
{
type: 'text',
text: `Type "${validatedArgs.name}" deleted successfully`
}
]
};
}
catch (error) {
if (error instanceof McpError) {
throw error;
}
if (error instanceof Error) {
throw new McpError(ErrorCode.InvalidRequest, error.message);
}
throw error;
}
}
}