@boundless-oss/atlas
Version:
Atlas - MCP Server for comprehensive startup project management
426 lines • 14.5 kB
JavaScript
/**
* Convert our internal ToolResult format to MCP SDK format with new schema
*/
export function convertToolResultToMCP(result, operation) {
// Check if data already follows the new MCPToolResponse format
if (result.success && result.data && typeof result.data === 'object' &&
'status' in result.data && 'message' in result.data) {
// Data is already in MCPToolResponse format
const response = result.data;
return {
content: [
{
type: 'text',
text: JSON.stringify(response, null, 2)
}
],
structured: response
};
}
// Convert legacy format to new schema
const timestamp = new Date().toISOString();
if (result.success) {
const response = {
status: 'success',
message: formatSuccessMessage(result.data, operation),
data: result.data,
metadata: {
operation: operation || 'unknown',
timestamp,
...getOperationMetadata(operation, result.data)
}
};
return {
content: [
{
type: 'text',
text: JSON.stringify(response, null, 2)
}
],
structured: response
};
}
else {
const response = {
status: 'error',
message: result.error?.message || 'An unknown error occurred',
error: {
code: result.error?.code || 'UNKNOWN_ERROR',
category: result.error?.category || 'system',
details: result.error?.details,
suggestions: result.error?.suggestions
},
metadata: {
operation: operation || 'unknown',
timestamp
}
};
return {
content: [
{
type: 'text',
text: JSON.stringify(response, null, 2)
}
],
structured: response
};
}
}
/**
* Format success data as a readable string
*/
function formatSuccessMessage(data, operation) {
if (data === null || data === undefined) {
return '✅ Operation completed successfully';
}
if (typeof data === 'string') {
return data;
}
if (typeof data === 'number' || typeof data === 'boolean') {
return String(data);
}
// Check if data has a message property for better formatting
if (data && typeof data === 'object' && 'message' in data) {
return data.message;
}
// For complex objects, provide a summary instead of full JSON
if (data && typeof data === 'object') {
const summary = formatDataSummary(data, operation);
if (summary) {
return summary;
}
}
// For objects and arrays, pretty print as JSON
try {
return JSON.stringify(data, null, 2);
}
catch (error) {
return '✅ Operation completed successfully (data available in structured field)';
}
}
/**
* Format complex data as a human-readable summary
*/
function formatDataSummary(data, operation) {
// Handle list responses
if (data.stories && Array.isArray(data.stories)) {
const count = data.stories.length;
const summary = data.summary;
return `✅ Retrieved ${count} user ${count === 1 ? 'story' : 'stories'}${summary ? `\n\nSummary:\n${JSON.stringify(summary, null, 2)}` : ''}`;
}
if (data.sprints && Array.isArray(data.sprints)) {
return `✅ Retrieved ${data.sprints.length} sprint(s)`;
}
if (data.epics && Array.isArray(data.epics)) {
return `✅ Retrieved ${data.epics.length} epic(s)`;
}
if (data.boards && Array.isArray(data.boards)) {
return `✅ Retrieved ${data.boards.length} kanban board(s)`;
}
if (data.tasks && Array.isArray(data.tasks)) {
return `✅ Retrieved ${data.tasks.length} task(s)`;
}
// Handle single entity responses based on operation
if (data.sprint && data.sprint.id) {
const verb = getOperationVerb(operation);
return `✅ ${verb} sprint "${data.sprint.name}" (ID: ${data.sprint.id})${data.nextSteps ? '\n\nNext steps:\n' + data.nextSteps.map((s) => `• ${s}`).join('\n') : ''}`;
}
if (data.story && data.story.id) {
const verb = getOperationVerb(operation);
return `✅ ${verb} user story "${data.story.title}" (ID: ${data.story.id})${data.nextSteps ? '\n\nNext steps:\n' + data.nextSteps.map((s) => `• ${s}`).join('\n') : ''}`;
}
if (data.epic && data.epic.id) {
const verb = getOperationVerb(operation);
return `✅ ${verb} epic "${data.epic.title}" (ID: ${data.epic.id})`;
}
// Handle board responses
if (data.board && data.board.id) {
const verb = getOperationVerb(operation);
return `✅ ${verb} kanban board "${data.board.name}" (ID: ${data.board.id})`;
}
// Handle task responses
if (data.task && data.task.id) {
const verb = getOperationVerb(operation);
return `✅ ${verb} task "${data.task.title}" (ID: ${data.task.id})`;
}
// Handle specific agile operations
if (operation === 'update_story_status' && data.oldStatus && data.newStatus) {
return `✅ Updated story status from "${data.oldStatus}" to "${data.newStatus}"${data.storyId ? ` (ID: ${data.storyId})` : ''}`;
}
if (operation === 'add_story_to_sprint' && data.message) {
return `✅ ${data.message}`;
}
if (operation === 'assign_story' && data.storyId && data.assignee) {
return `✅ Assigned story to ${data.assignee}${data.storyTitle ? ` - "${data.storyTitle}"` : ''}`;
}
if (operation === 'block_story' && data.blocker) {
return `✅ Blocked story "${data.blocker.storyTitle || data.blocker.storyId}" - ${data.blocker.reason}`;
}
if (operation === 'complete_sprint' && data.sprint) {
return `✅ Completed sprint "${data.sprint.name}" with ${data.completedStories || 0} stories done`;
}
// Handle generic update responses
if (data.updated && typeof data.updated === 'object') {
const type = data.updated.type || 'item';
const id = data.updated.id;
return `✅ Updated ${type}${id ? ` (ID: ${id})` : ''}`;
}
// Handle update responses with entity data
if (data.updated === true && (data.story || data.epic || data.sprint || data.task || data.board)) {
const entity = data.story || data.epic || data.sprint || data.task || data.board;
const entityType = data.story ? 'story' : data.epic ? 'epic' : data.sprint ? 'sprint' : data.task ? 'task' : 'board';
const title = entity.title || entity.name || entity.id;
return `✅ Updated ${entityType} "${title}"${entity.id ? ` (ID: ${entity.id})` : ''}`;
}
// Handle deletion responses
if (data.deleted) {
return `✅ Deleted successfully`;
}
// Handle other responses with a result count
const keys = Object.keys(data);
if (keys.length === 1 && Array.isArray(data[keys[0]])) {
const items = data[keys[0]];
return `✅ Retrieved ${items.length} ${keys[0]}`;
}
return null;
}
/**
* Format error information as a readable string
*/
function formatErrorMessage(error) {
if (!error) {
return '❌ An unknown error occurred';
}
const parts = [];
// Add main error message
parts.push(`❌ Error: ${error.message || 'Unknown error'}`);
// Add error code if available
if (error.code) {
parts.push(`Code: ${error.code}`);
}
// Add category if available
if (error.category) {
parts.push(`Category: ${error.category}`);
}
// Add suggestions if available
if (error.suggestions && Array.isArray(error.suggestions) && error.suggestions.length > 0) {
parts.push('\nSuggestions:');
error.suggestions.forEach((suggestion, index) => {
parts.push(`${index + 1}. ${suggestion}`);
});
}
return parts.join('\n');
}
/**
* Create a simple MCP response with just text
*/
export function createMCPTextResponse(text) {
return {
content: [
{
type: 'text',
text: text
}
]
};
}
/**
* Create an MCP error response
*/
export function createMCPErrorResponse(message, details) {
return {
content: [
{
type: 'text',
text: `❌ Error: ${message}`
}
],
structured: details
};
}
/**
* Helper to create a standardized success response
*/
export function createMCPSuccessResponse(message, data, metadata) {
return {
status: 'success',
message,
data,
metadata: {
...metadata,
timestamp: new Date().toISOString()
}
};
}
/**
* Helper to create a standardized error response
*/
export function createMCPErrorResponseV2(message, error, operation) {
return {
status: 'error',
message,
error,
metadata: {
operation,
timestamp: new Date().toISOString()
}
};
}
/**
* Get appropriate verb for an operation
*/
function getOperationVerb(operation) {
if (!operation)
return 'Processed';
// Check for specific operation types
if (operation.startsWith('create_'))
return 'Created';
if (operation.startsWith('get_'))
return 'Retrieved';
if (operation.startsWith('list_'))
return 'Listed';
if (operation.startsWith('update_'))
return 'Updated';
if (operation.startsWith('delete_'))
return 'Deleted';
if (operation.startsWith('add_'))
return 'Added';
if (operation.startsWith('remove_'))
return 'Removed';
if (operation.startsWith('move_'))
return 'Moved';
if (operation.startsWith('assign_'))
return 'Assigned';
if (operation.startsWith('complete_'))
return 'Completed';
if (operation.startsWith('close_'))
return 'Closed';
if (operation.startsWith('block_'))
return 'Blocked';
if (operation.startsWith('split_'))
return 'Split';
if (operation.startsWith('link_'))
return 'Linked';
if (operation.startsWith('generate_'))
return 'Generated';
if (operation.startsWith('validate_'))
return 'Validated';
if (operation.startsWith('analyze_'))
return 'Analyzed';
// Default
return 'Processed';
}
/**
* Get operation-specific metadata based on tool name and data
*/
function getOperationMetadata(operation, data) {
if (!operation)
return {};
// Agile management tools
if (operation.includes('sprint')) {
if (operation === 'create_agile_sprint') {
return {
dataType: 'sprint',
relatedTools: ['add_story_to_sprint', 'list_stories', 'get_sprint_stats']
};
}
if (operation === 'list_agile_sprints') {
return {
dataType: 'sprint_list',
relatedTools: ['get_sprint', 'create_agile_sprint', 'get_sprint_stats']
};
}
}
if (operation.includes('story')) {
if (operation === 'create_user_story') {
return {
dataType: 'story',
relatedTools: ['add_story_to_sprint', 'update_story_status', 'list_stories']
};
}
if (operation === 'list_stories') {
return {
dataType: 'story_list',
relatedTools: ['create_user_story', 'get_story', 'update_story_status']
};
}
}
if (operation.includes('epic')) {
if (operation === 'create_epic') {
return {
dataType: 'epic',
relatedTools: ['list_epics', 'create_user_story', 'update_epic']
};
}
if (operation === 'list_epics') {
return {
dataType: 'epic_list',
relatedTools: ['create_epic', 'get_epic', 'update_epic']
};
}
}
// Task management tools
if (operation.includes('board')) {
if (operation === 'create_kanban_board') {
return {
dataType: 'board',
relatedTools: ['create_kanban_task', 'get_kanban_board', 'list_boards']
};
}
if (operation === 'list_boards') {
return {
dataType: 'board_list',
relatedTools: ['create_kanban_board', 'get_kanban_board', 'get_kanban_analytics']
};
}
}
if (operation.includes('task')) {
if (operation === 'create_kanban_task') {
return {
dataType: 'task',
relatedTools: ['move_kanban_task', 'update_task', 'get_tasks']
};
}
if (operation === 'get_tasks' || operation === 'list_tasks') {
return {
dataType: 'task_list',
relatedTools: ['create_kanban_task', 'update_task', 'move_kanban_task']
};
}
}
// Memory management
if (operation.includes('memory')) {
if (operation === 'store_memory') {
return {
dataType: 'memory',
relatedTools: ['search_memories', 'get_memory', 'get_memory_stats']
};
}
if (operation === 'search_memories') {
return {
dataType: 'memory_list',
relatedTools: ['store_memory', 'get_memory', 'get_memory_stats']
};
}
}
// Business management
if (operation.includes('business')) {
if (operation === 'generate_business_plan') {
return {
dataType: 'business_plan',
relatedTools: ['analyze_market', 'competitor_research', 'financial_projections']
};
}
if (operation === 'perform_business_review') {
return {
dataType: 'business_review',
relatedTools: ['track_startup_metrics', 'generate_business_plan', 'startup_assessment']
};
}
}
// Default metadata
return {
dataType: 'generic_response'
};
}
//# sourceMappingURL=mcp-adapter.js.map