mcp-casual-interview
Version:
MCP server for casual interview preparation workflow management
99 lines • 5.1 kB
JavaScript
import { toStructuredCallToolResult } from '../util.js';
import { formatProgressUpdateMessage, errorMessages } from './prompts.js';
// Domain layer imports
import { WorkflowStateEffects, ErrorMapping } from '../../../effect/workflow-state/index.js';
import { WorkflowPolicies } from '../../../domain/term/interview-workflow/index.js';
import { ProgressViewQueries } from '../../../domain/read/progress-view/index.js';
/**
* Maps completed step to next workflow state
*/
const getNextWorkflowState = (completedStep) => {
return WorkflowPolicies.StateTransitions.getNextState(completedStep);
};
/**
* Maps domain WorkflowState to schema ResponseWorkflowStateType
*/
const mapWorkflowStateToSchemaType = (domainState) => {
const mapping = {
'not_started': 'not_started',
'information_initially_registered': 'initial_info_registered',
'career_summary_created': 'career_summary_created',
'match_position_determined': 'match_position_determined',
'match_degree_prediagnosed': 'match_degree_diagnosed',
'attract_plan_determined': 'attract_plan_determined',
'completed': 'completed'
};
return mapping[domainState];
};
/**
* Maps domain progress response to MCP output schema
*/
const mapProgressResponseToOutput = (currentState, nextAction) => {
return {
currentState: mapWorkflowStateToSchemaType(currentState),
nextAction: nextAction
};
};
/**
* Progress update handler using domain layer orchestration
*/
export const progressUpdateHandler = async (args) => {
// 1. Get current workflow state from effect layer
const currentStateResult = await WorkflowStateEffects.getWorkflowState();
if (currentStateResult.isErr()) {
const workflowError = ErrorMapping.mapStorageErrorToWorkflowError(currentStateResult.error);
return toStructuredCallToolResult([errorMessages.storageError(workflowError.message)], { error: workflowError.message }, true);
}
const previousState = currentStateResult.value;
// 2. Set completed step as the new current state
const newCurrentState = args.completedStep;
// 3. Validate state transition to the completed step
if (!WorkflowPolicies.StateTransitions.canTransition(previousState, newCurrentState)) {
const nextValidState = WorkflowPolicies.StateTransitions.getNextState(previousState);
const errorMessage = nextValidState
? errorMessages.invalidTransitionWithGuidance(previousState, newCurrentState, nextValidState)
: errorMessages.invalidTransition(previousState, newCurrentState);
return toStructuredCallToolResult([errorMessage], { error: 'Invalid state transition', currentState: previousState, expectedNext: nextValidState }, true);
}
// 4. Check if step is already completed (idempotency check)
const previousStateMapping = {
'not_started': 'not_started',
'information_initially_registered': 'initial_info_registered',
'career_summary_created': 'career_summary_created',
'match_position_determined': 'match_position_determined',
'match_degree_prediagnosed': 'match_degree_diagnosed',
'attract_plan_determined': 'attract_plan_determined',
'completed': 'completed'
};
const requestedStepName = args.completedStep;
if (WorkflowPolicies.StateTransitions.canTransition(requestedStepName, previousState) === false && previousState !== requestedStepName) {
// If we can't transition from requested step to current state, it means the step might already be completed
const currentMappedState = previousStateMapping[previousState];
if (currentMappedState !== 'initial_info_registered') { // Allow restarting from initial state
return toStructuredCallToolResult([errorMessages.alreadyCompleted(requestedStepName)], { warning: 'Step already completed' }, false);
}
}
// 5. Update workflow state in effect layer
const updateResult = await WorkflowStateEffects.setWorkflowState(newCurrentState);
if (updateResult.isErr()) {
const workflowError = ErrorMapping.mapStorageErrorToWorkflowError(updateResult.error);
return toStructuredCallToolResult([errorMessages.storageError(workflowError.message)], { error: workflowError.message }, true);
}
// 6. Build read model view from new state
const progressView = ProgressViewQueries.fromState(newCurrentState);
// 7. Map domain data to MCP output schema
const outputData = mapProgressResponseToOutput(newCurrentState, progressView.nextAction.message);
// 7.1. Add notion URL if provided
if (args.notionUrl) {
outputData.notionUrl = args.notionUrl;
}
// 8. Format for human consumption
const message = formatProgressUpdateMessage({
previousState: mapWorkflowStateToSchemaType(previousState),
currentState: outputData.currentState,
nextAction: outputData.nextAction,
notionUrl: args.notionUrl
});
return toStructuredCallToolResult([message], outputData, false);
};
//# sourceMappingURL=handler.js.map