@mseep/atlas-mcp-server
Version:
A Model Context Protocol (MCP) server for ATLAS, a Neo4j-powered task management system for LLM Agents - implementing a three-tier architecture (Projects, Tasks, Knowledge) to manage complex workflows.
170 lines (146 loc) • 4.79 kB
text/typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { ProjectService } from "../../../services/neo4j/projectService.js";
import { toProjectResource, ResourceTemplates, ResourceURIs } from "../types.js";
import { logger } from "../../../utils/logger.js";
import { BaseErrorCode, McpError, ProjectErrorCode } from "../../../types/errors.js";
/**
* Register Project Resources
*
* This function registers resource endpoints for the Projects entity
* - GET atlas://projects - List all projects
* - GET atlas://projects/{projectId} - Get specific project by ID
*
* @param server The MCP server instance
*/
export function registerProjectResources(server: McpServer) {
// List all projects
server.resource(
"projects-list",
ResourceURIs.PROJECTS,
{
name: "All Projects",
description: "List of all projects in the Atlas platform with pagination support",
mimeType: "application/json"
},
async (uri) => {
try {
logger.info("Listing projects", { uri: uri.href });
// Parse query parameters
const queryParams = new URLSearchParams(uri.search);
const filters: Record<string, any> = {};
// Parse status parameter
const status = queryParams.get("status");
if (status) {
filters.status = String(status);
}
// Parse taskType parameter
const taskType = queryParams.get("taskType");
if (taskType) {
filters.taskType = String(taskType);
}
// Parse pagination parameters
const page = queryParams.has("page")
? parseInt(queryParams.get("page") || "1", 10)
: 1;
const limit = queryParams.has("limit")
? parseInt(queryParams.get("limit") || "20", 10)
: 20;
// Add pagination to filters
filters.page = page;
filters.limit = limit;
// Use ProjectService instead of direct database access
const result = await ProjectService.getProjects(filters);
// Convert Neo4j projects to project resources
const projectResources = result.data.map(toProjectResource);
// Create pagination metadata using result from service
const pagination = {
total: result.total,
page: result.page,
limit: result.limit,
totalPages: result.totalPages
};
logger.info(`Found ${result.total} projects, returning page ${result.page} with ${projectResources.length} items`);
return {
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify({
projects: projectResources,
pagination
}, null, 2)
}
]
};
} catch (error) {
logger.error("Error listing projects", {
error,
uri: uri.href
});
throw new McpError(
BaseErrorCode.INTERNAL_ERROR,
`Failed to list projects: ${error instanceof Error ? error.message : String(error)}`
);
}
});
// Get project by ID
server.resource(
"project-by-id",
ResourceTemplates.PROJECT,
{
name: "Project by ID",
description: "Retrieves a single project by its unique identifier",
mimeType: "application/json"
},
async (uri, params) => {
try {
const projectId = params.projectId as string;
logger.info("Fetching project by ID", {
projectId,
uri: uri.href
});
if (!projectId) {
throw new McpError(
BaseErrorCode.VALIDATION_ERROR,
"Project ID is required"
);
}
// Use ProjectService instead of direct database access
const project = await ProjectService.getProjectById(projectId);
if (!project) {
throw new McpError(
ProjectErrorCode.PROJECT_NOT_FOUND,
`Project with ID ${projectId} not found`,
{ projectId }
);
}
// Convert to resource format
const projectResource = toProjectResource(project);
logger.info("Retrieved project successfully", {
projectId: project.id
});
return {
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify(projectResource, null, 2)
}
]
};
} catch (error) {
// Handle specific error cases
if (error instanceof McpError) {
throw error;
}
logger.error("Error fetching project by ID", {
error,
params
});
throw new McpError(
BaseErrorCode.INTERNAL_ERROR,
`Failed to fetch project: ${error instanceof Error ? error.message : String(error)}`
);
}
});
}