This is page 1 of 6. Use http://codebase.md/cyanheads/atlas-mcp-server?lines=false&page={x} to view the full context.
# Directory Structure
```
├── .clinerules
├── .dockerignore
├── .env.example
├── .github
│ ├── FUNDING.yml
│ └── workflows
│ └── publish.yml
├── .gitignore
├── .ncurc.json
├── .repomixignore
├── automated-tests
│ └── AGENT_TEST_05282025.md
├── CHANGELOG.md
├── CLAUDE.md
├── docker-compose.yml
├── docs
│ └── tree.md
├── examples
│ ├── backup-example
│ │ ├── knowledges.json
│ │ ├── projects.json
│ │ ├── relationships.json
│ │ └── tasks.json
│ ├── deep-research-example
│ │ ├── covington_community_grant_research.md
│ │ └── full-export.json
│ ├── README.md
│ └── webui-example.png
├── LICENSE
├── mcp.json
├── package-lock.json
├── package.json
├── README.md
├── repomix.config.json
├── scripts
│ ├── clean.ts
│ ├── fetch-openapi-spec.ts
│ ├── make-executable.ts
│ └── tree.ts
├── smithery.yaml
├── src
│ ├── config
│ │ └── index.ts
│ ├── index.ts
│ ├── mcp
│ │ ├── resources
│ │ │ ├── index.ts
│ │ │ ├── knowledge
│ │ │ │ └── knowledgeResources.ts
│ │ │ ├── projects
│ │ │ │ └── projectResources.ts
│ │ │ ├── tasks
│ │ │ │ └── taskResources.ts
│ │ │ └── types.ts
│ │ ├── server.ts
│ │ ├── tools
│ │ │ ├── atlas_database_clean
│ │ │ │ ├── cleanDatabase.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ └── types.ts
│ │ │ ├── atlas_deep_research
│ │ │ │ ├── deepResearch.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ └── types.ts
│ │ │ ├── atlas_knowledge_add
│ │ │ │ ├── addKnowledge.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ └── types.ts
│ │ │ ├── atlas_knowledge_delete
│ │ │ │ ├── deleteKnowledge.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ └── types.ts
│ │ │ ├── atlas_knowledge_list
│ │ │ │ ├── index.ts
│ │ │ │ ├── listKnowledge.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ └── types.ts
│ │ │ ├── atlas_project_create
│ │ │ │ ├── createProject.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ └── types.ts
│ │ │ ├── atlas_project_delete
│ │ │ │ ├── deleteProject.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ └── types.ts
│ │ │ ├── atlas_project_list
│ │ │ │ ├── index.ts
│ │ │ │ ├── listProjects.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ └── types.ts
│ │ │ ├── atlas_project_update
│ │ │ │ ├── index.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── updateProject.ts
│ │ │ ├── atlas_task_create
│ │ │ │ ├── createTask.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ └── types.ts
│ │ │ ├── atlas_task_delete
│ │ │ │ ├── deleteTask.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ └── types.ts
│ │ │ ├── atlas_task_list
│ │ │ │ ├── index.ts
│ │ │ │ ├── listTasks.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ └── types.ts
│ │ │ ├── atlas_task_update
│ │ │ │ ├── index.ts
│ │ │ │ ├── responseFormat.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── updateTask.ts
│ │ │ └── atlas_unified_search
│ │ │ ├── index.ts
│ │ │ ├── responseFormat.ts
│ │ │ ├── types.ts
│ │ │ └── unifiedSearch.ts
│ │ └── transports
│ │ ├── authentication
│ │ │ └── authMiddleware.ts
│ │ ├── httpTransport.ts
│ │ └── stdioTransport.ts
│ ├── services
│ │ └── neo4j
│ │ ├── backupRestoreService
│ │ │ ├── backupRestoreTypes.ts
│ │ │ ├── backupUtils.ts
│ │ │ ├── exportLogic.ts
│ │ │ ├── importLogic.ts
│ │ │ ├── index.ts
│ │ │ └── scripts
│ │ │ ├── db-backup.ts
│ │ │ └── db-import.ts
│ │ ├── driver.ts
│ │ ├── events.ts
│ │ ├── helpers.ts
│ │ ├── index.ts
│ │ ├── knowledgeService.ts
│ │ ├── projectService.ts
│ │ ├── searchService
│ │ │ ├── fullTextSearchLogic.ts
│ │ │ ├── index.ts
│ │ │ ├── searchTypes.ts
│ │ │ └── unifiedSearchLogic.ts
│ │ ├── taskService.ts
│ │ ├── types.ts
│ │ └── utils.ts
│ ├── types
│ │ ├── errors.ts
│ │ ├── mcp.ts
│ │ └── tool.ts
│ ├── utils
│ │ ├── index.ts
│ │ ├── internal
│ │ │ ├── errorHandler.ts
│ │ │ ├── index.ts
│ │ │ ├── logger.ts
│ │ │ └── requestContext.ts
│ │ ├── metrics
│ │ │ ├── index.ts
│ │ │ └── tokenCounter.ts
│ │ ├── parsing
│ │ │ ├── dateParser.ts
│ │ │ ├── index.ts
│ │ │ └── jsonParser.ts
│ │ └── security
│ │ ├── idGenerator.ts
│ │ ├── index.ts
│ │ ├── rateLimiter.ts
│ │ └── sanitization.ts
│ └── webui
│ ├── index.html
│ ├── logic
│ │ ├── api-service.js
│ │ ├── app-state.js
│ │ ├── config.js
│ │ ├── dom-elements.js
│ │ ├── main.js
│ │ └── ui-service.js
│ └── styling
│ ├── base.css
│ ├── components.css
│ ├── layout.css
│ └── theme.css
├── tsconfig.json
├── tsconfig.typedoc.json
└── typedoc.json
```
# Files
--------------------------------------------------------------------------------
/.ncurc.json:
--------------------------------------------------------------------------------
```json
{
"reject": ["chrono-node"]
}
```
--------------------------------------------------------------------------------
/.repomixignore:
--------------------------------------------------------------------------------
```
# Environment and secrets
.env
.env.*
!.env.example
.venv/
.venv3/
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
.python-version
venv/
env/
ENV/
# IDE
.idea/
.vscode/
*.swp
*.swo
.DS_Store
# Project specific
logs/
output/
data/*
!data/.gitkeep
# LaTeX
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
*.fmt
*.fot
*.cb
*.cb2
.*.lb
*.dvi
*.xdv
*-converted-to.*
*.pdf
*.fdb_latexmk
*.synctex
*.synctex(busy)
*.synctex.gz
*.synctex.gz(busy)
*.pdfsync
latex.out/
node_modules/
# Misc
repomix/
repomix-output.txt
repomix-output.xml
repomix-output.json
logs/
ideas/
backups/
src/mcp/
```
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
```
# Neo4j Configuration
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=password2
# Application Configuration
LOG_LEVEL=info # Log level: emerg, alert, crit, error, warning, notice, info, debug (corresponds to MCP_LOG_LEVEL)
NODE_ENV=development # development, production
# MCP Transport Configuration
MCP_TRANSPORT_TYPE=stdio # 'stdio' or 'http'. Default: stdio
MCP_HTTP_HOST=127.0.0.1 # Host for HTTP transport. Default: 127.0.0.1
MCP_HTTP_PORT=3010 # Port for HTTP transport. Default: 3010
# MCP_ALLOWED_ORIGINS=http://localhost:3000,https://your-client.com # Comma-separated list of allowed origins for HTTP CORS. Optional.
# MCP Security Configuration
# MCP_AUTH_SECRET_KEY=your_very_long_and_secure_secret_key_min_32_chars # Secret key for JWT authentication (if HTTP transport is used and auth is desired). Optional.
MCP_RATE_LIMIT_WINDOW_MS=60000 # Rate limit window in milliseconds. Default: 60000 (1 minute)
MCP_RATE_LIMIT_MAX_REQUESTS=100 # Max requests per window per IP. Default: 100
# Database Backup Configuration
BACKUP_MAX_COUNT=10 # Maximum number of backup files to keep. Default: 10
BACKUP_FILE_DIR=./backups # Directory where backup files will be stored (relative to project root). Default: ./backups
```
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
```
# =============================================================================
# OPERATING SYSTEM FILES
# =============================================================================
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# =============================================================================
# IDE AND EDITOR FILES
# =============================================================================
.idea/
.vscode/
*.swp
*.swo
*~
*.sublime-workspace
*.sublime-project
.history/
# =============================================================================
# NODE.JS & PACKAGE MANAGERS
# =============================================================================
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
.npm
.pnp.js
.pnp.cjs
.pnp.mjs
.pnp.json
.pnp.ts
# =============================================================================
# TYPESCRIPT & JAVASCRIPT
# =============================================================================
*.tsbuildinfo
.tscache/
*.js.map
*.mjs.map
*.cjs.map
*.d.ts.map
*.d.ts
!*.d.ts.template
*.tgz
.eslintcache
.rollup.cache
# =============================================================================
# PYTHON
# =============================================================================
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
develop-eggs/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
.pytest_cache/
.coverage
htmlcov/
.tox/
.venv
venv/
ENV/
# =============================================================================
# JAVA
# =============================================================================
*.class
*.jar
*.war
*.nar
*.ear
hs_err_pid*
target/
.gradle/
# =============================================================================
# RUBY
# =============================================================================
*.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/
.byebug_history
# =============================================================================
# BUILD & DISTRIBUTION
# =============================================================================
build/
dist/
out/
# =============================================================================
# COMPILED FILES
# =============================================================================
*.com
*.dll
*.exe
*.o
# =============================================================================
# PACKAGE & ARCHIVE FILES
# =============================================================================
*.7z
*.dmg
*.gz
*.iso
*.rar
*.tar
*.tar.gz
*.zip
# =============================================================================
# LOGS & DATABASES
# =============================================================================
*.log
*.sql
*.sqlite
*.sqlite3
logs/
# =============================================================================
# TESTING & COVERAGE
# =============================================================================
coverage/
.nyc_output/
# =============================================================================
# CACHE & TEMPORARY FILES
# =============================================================================
.cache/
.parcel-cache/
*.bak
# =============================================================================
# ENVIRONMENT & CONFIGURATION
# =============================================================================
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.sample-env
sample.*
!sample.template.*
mcp-servers.json
mcp-config.json
# =============================================================================
# DEMO & EXAMPLE DIRECTORIES
# =============================================================================
demo/
demos/
example/
examples/
samples/
# =============================================================================
# GENERATED DOCUMENTATION
# =============================================================================
docs/api/
# =============================================================================
# APPLICATION SPECIFIC
# =============================================================================
repomix-output*
duckdata/
.claude
*atlas-backups*
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
# =============================================================================
# OPERATING SYSTEM FILES
# =============================================================================
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# =============================================================================
# IDE AND EDITOR FILES
# =============================================================================
.idea/
.vscode/
*.swp
*.swo
*~
*.sublime-workspace
*.sublime-project
.history/
# =============================================================================
# NODE.JS & PACKAGE MANAGERS
# =============================================================================
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
.npm
.pnp.js
.pnp.cjs
.pnp.mjs
.pnp.json
.pnp.ts
# =============================================================================
# TYPESCRIPT & JAVASCRIPT
# =============================================================================
*.tsbuildinfo
.tscache/
*.js.map
*.mjs.map
*.cjs.map
*.d.ts.map
*.d.ts
!*.d.ts.template
*.tgz
.eslintcache
.rollup.cache
# =============================================================================
# PYTHON
# =============================================================================
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
develop-eggs/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
.pytest_cache/
.coverage
htmlcov/
.tox/
.venv
venv/
ENV/
# =============================================================================
# JAVA
# =============================================================================
*.class
*.jar
*.war
*.nar
*.ear
hs_err_pid*
target/
.gradle/
# =============================================================================
# RUBY
# =============================================================================
*.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/
.byebug_history
# =============================================================================
# BUILD & DISTRIBUTION
# =============================================================================
build/
dist/
out/
# =============================================================================
# COMPILED FILES
# =============================================================================
*.com
*.dll
*.exe
*.o
# =============================================================================
# PACKAGE & ARCHIVE FILES
# =============================================================================
*.7z
*.dmg
*.gz
*.iso
*.rar
*.tar
*.tar.gz
*.zip
# =============================================================================
# LOGS & DATABASES
# =============================================================================
*.log
*.sql
*.sqlite
*.sqlite3
logs/
# =============================================================================
# TESTING & COVERAGE
# =============================================================================
coverage/
.nyc_output/
# =============================================================================
# CACHE & TEMPORARY FILES
# =============================================================================
.cache/
.parcel-cache/
*.bak
# =============================================================================
# ENVIRONMENT & CONFIGURATION
# =============================================================================
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.sample-env
sample.*
!sample.template.*
mcp-servers.json
mcp-config.json
# =============================================================================
# DEMO & EXAMPLE DIRECTORIES
# =============================================================================
demo/
demos/
example/
examples/
samples/
# =============================================================================
# GENERATED DOCUMENTATION
# =============================================================================
docs/api/
# =============================================================================
# APPLICATION SPECIFIC
# =============================================================================
repomix-output*
duckdata/
.claude
*atlas-backups*
```
--------------------------------------------------------------------------------
/.clinerules:
--------------------------------------------------------------------------------
```
# Atlas MCP Server Developer Cheat Sheet
This document provides essential guidelines and code snippets for developers working within the Atlas MCP Server repository. Adhering to these conventions ensures consistency, maintainability, and security.
## 1. Request Context (`src/utils/internal/requestContext.ts`)
All operations that handle requests or perform distinct units of work MUST utilize a `RequestContext`. This context provides a unique `requestId` for tracing and a `timestamp`.
**Interface:**
```typescript
export interface RequestContext {
requestId: string;
timestamp: string;
[key: string]: unknown; // For additional context-specific data
}
```
**Creating a Request Context:**
Use the `requestContextService` to create new contexts.
```typescript
import { requestContextService } from './src/utils/index.js'; // Or specific path
// Basic context
const context = requestContextService.createRequestContext();
// { requestId: "uuid-v4-string", timestamp: "iso-string" }
// Context with additional data
const userContext = requestContextService.createRequestContext({
userId: "user123",
operationName: "getUserProfile"
});
// { requestId: "uuid-v4-string", timestamp: "iso-string", userId: "user123", operationName: "getUserProfile" }
```
**Usage:**
Pass the `RequestContext` object (or its `requestId`) to logging functions and through service layers to correlate operations.
## 2. Logging (`src/utils/internal/logger.ts`)
Our `logger` is a singleton instance of a Winston-based logger, adapted for MCP. It supports multiple log levels and can send MCP notifications.
**Log Levels (RFC 5424):**
`emerg` (0), `alert` (1), `crit` (2), `error` (3), `warning` (4), `notice` (5), `info` (6), `debug` (7)
**Initialization (in `src/index.ts`):**
The logger is initialized within the `start` function in `src/index.ts`.
```typescript
// In src/index.ts
import { logger } from './src/utils/index.js';
import { config } from './src/config/index.js'; // McpLogLevel is typically part of config types
async function start() {
// ... other setup ...
await logger.initialize(config.logLevel); // config.logLevel is McpLogLevel
// ... rest of app initialization
}
```
**Using the Logger:**
Always provide meaningful messages. For errors, include the `Error` object. Contextual information should be passed as the second argument (an object).
```typescript
import { logger } from './src/utils/index.js';
import { requestContextService } from './src/utils/index.js';
const requestContext = requestContextService.createRequestContext({ userId: 'user-abc' });
// Debug log
logger.debug('Processing user request', { requestId: requestContext.requestId, details: 'some detail' });
// Info log
logger.info('User authenticated successfully', { userId: requestContext.userId, requestId: requestContext.requestId });
// Warning log
logger.warning('User session nearing expiration', { userId: 'user-xyz', expiresIn: '5m', requestId: requestContext.requestId });
// Error log
try {
// ... some operation that might fail ...
throw new Error("Something went wrong");
} catch (err) {
// The 'err' parameter in logger.error can be an Error instance or an object for additional context.
// If 'err' is an Error instance, its message and stack will be logged.
// The third parameter 'context' is for additional structured data.
logger.error('Failed to process payment', err as Error, {
requestId: requestContext.requestId,
paymentId: 'payment-123'
});
}
// Critical error
const criticalError = new Error("Database connection lost");
logger.crit('Critical database failure', criticalError, { component: 'DatabaseService', requestId: requestContext.requestId });
// Fatal error (alias for emerg)
logger.fatal('System shutting down due to unrecoverable error', { reason: 'disk full' }, criticalError);
```
**Log Output:**
- Logs are written to files in the `logs/` directory (e.g., `error.log`, `combined.log`).
- Console logging is active if `logLevel` is `debug` AND `stdout` is a TTY.
- Sensitive data in log context is automatically redacted (see Sanitization).
## 3. Error Handling (`src/utils/internal/errorHandler.ts` & `src/types/errors.ts`)
We use a standardized approach to error handling, centered around `McpError` and the `ErrorHandler` utility.
**`McpError` (`src/types/errors.ts`):**
Custom error class that includes an error `code` (from `BaseErrorCode` or feature-specific enums) and optional `details`.
```typescript
import { McpError, BaseErrorCode } from './src/types/errors.js';
throw new McpError(
BaseErrorCode.VALIDATION_ERROR,
"User ID is missing.",
{ field: "userId", inputReceived: { username: "test" } }
);
```
**`ErrorHandler.handleError`:**
Centralized function to log errors consistently and optionally rethrow them.
```typescript
import { ErrorHandler } from './src/utils/internal/errorHandler.js';
import { BaseErrorCode } from './src/types/errors.js';
import { requestContextService } from './src/utils/index.js';
const requestContext = requestContextService.createRequestContext();
async function someOperation(data: any) {
try {
if (!data.id) {
throw new Error("ID is required for this operation.");
}
// ... perform operation ...
} catch (error) {
// Handle and rethrow as McpError
throw ErrorHandler.handleError(error, {
operation: 'someOperation',
context: { requestId: requestContext.requestId, customInfo: 'some_value' },
input: data, // Will be sanitized for logging
errorCode: BaseErrorCode.VALIDATION_ERROR, // Optionally override determined code
rethrow: true, // Default is false
critical: false, // Default is false
});
}
}
```
**Key `ErrorHandlerOptions`:**
- `operation`: (string, required) Name of the operation (e.g., "UserLogin", "CreateTask").
- `context`: (ErrorContext, optional) Includes `requestId` and other debug info.
- `input`: (unknown, optional) Input data that caused the error (sanitized before logging).
- `rethrow`: (boolean, optional, default: `false`) If `true`, rethrows the (potentially transformed) error.
- `errorCode`: (BaseErrorCode, optional) Explicitly set the error code.
- `critical`: (boolean, optional, default: `false`) Mark as critical for logging/alerting.
**`ErrorHandler.tryCatch`:**
A wrapper to simplify try-catch blocks for asynchronous or synchronous functions.
```typescript
import { ErrorHandler } from './src/utils/internal/errorHandler.js';
async function getUser(userId: string) {
// ... logic to get user ...
// if (!user) throw new Error("User not found"); // Example error
return { id: userId, name: "Test User" }; // Example success
}
const safeGetUser = (userId: string) => ErrorHandler.tryCatch(
() => getUser(userId),
{ operation: 'getUser', input: { userId } } // rethrow is true by default in tryCatch
);
try {
const user = await safeGetUser("123");
} catch (e) {
// e will be an McpError, logged by ErrorHandler.handleError
// console.error("Caught error:", (e as McpError).code, (e as McpError).message);
}
```
## 4. Sanitization (`src/utils/security/sanitization.ts`)
Input sanitization is CRITICAL for security. Use the `sanitization` service (from `src/utils/index.js` or `src/utils/security/sanitization.js`).
**`sanitizeInputForLogging(input: unknown): unknown`:**
Automatically used by `ErrorHandler.handleError` for the `input` field. Manually use it if logging raw input elsewhere. Redacts sensitive fields like 'password', 'token', etc.
```typescript
import { sanitizeInputForLogging, logger } from './src/utils/index.js';
const sensitiveData = { password: "supersecret", username: "admin" };
logger.info("User login attempt", { data: sanitizeInputForLogging(sensitiveData) });
// Log output for data.password will be "[REDACTED]"
```
**`sanitizeHtml(input: string, config?: HtmlSanitizeConfig): string`:**
For cleaning HTML strings.
```typescript
import { sanitization } from './src/utils/index.js';
const unsafeHtml = "<script>alert('xss')</script><p>Hello</p>";
const safeHtml = sanitization.sanitizeHtml(unsafeHtml);
// safeHtml will be "<p>Hello</p>"
```
**`sanitizeString(input: string, options?: SanitizeStringOptions): string`:**
General string sanitization. Context can be 'text', 'html', 'attribute', 'url'.
**NEVER use `context: 'javascript'`.**
```typescript
import { sanitization } from './src/utils/index.js';
const userInput = " Test User <img src=x onerror=alert(1)> ";
const safeText = sanitization.sanitizeString(userInput, { context: 'text' }); // "Test User <img src=x onerror=alert(1)>"
const safeAttribute = sanitization.sanitizeString("javascript:alert(1)", { context: 'attribute' }); // ""
```
**`sanitizeUrl(input: string, allowedProtocols?: string[]): string`:**
Validates and cleans URLs.
```typescript
import { sanitization } from './src/utils/index.js';
try {
const cleanUrl = sanitization.sanitizeUrl("https://example.com/path?query=value");
// const maliciousUrl = sanitization.sanitizeUrl("javascript:alert('evil')"); // Throws McpError
} catch (e) {
// logger.error("URL sanitization failed", e as Error);
}
```
**`sanitizePath(input: string, options?: PathSanitizeOptions): SanitizedPathInfo`:**
Crucial for handling file paths to prevent traversal.
```typescript
import { sanitization } from './src/utils/index.js';
try {
// Basic relative path
const pathInfo1 = sanitization.sanitizePath("some/file.txt");
// pathInfo1.sanitizedPath will be "some/file.txt"
// Attempted traversal (throws McpError by default if not rooted and escapes CWD)
// const pathInfo2 = sanitization.sanitizePath("../../../etc/passwd");
// Rooted path
const pathInfo3 = sanitization.sanitizePath("user/uploads/image.jpg", { rootDir: "/var/www/app" });
// pathInfo3.sanitizedPath will be "user/uploads/image.jpg" (assuming it's within rootDir)
const pathInfo4 = sanitization.sanitizePath("/absolute/path/file.txt", { allowAbsolute: true });
// pathInfo4.sanitizedPath will be "/absolute/path/file.txt"
} catch (e) {
// logger.error("Path sanitization failed", e as Error);
}
```
**`sanitizeJson<T = unknown>(input: string, maxSize?: number): T`:**
Parses JSON string safely, with optional size validation.
```typescript
import { sanitization } from './src/utils/index.js';
const jsonString = '{"name": "Test", "value": 123}';
try {
const jsonData = sanitization.sanitizeJson<{ name: string, value: number }>(jsonString);
} catch (e) {
// logger.error("JSON sanitization failed", e as Error);
}
```
**`sanitizeNumber(input: number | string, min?: number, max?: number): number`:**
Validates and clamps numbers.
```typescript
import { sanitization } from './src/utils/index.js';
const num1 = sanitization.sanitizeNumber("123.45"); // 123.45
const num2 = sanitization.sanitizeNumber("5", 10, 20); // 10 (clamped)
try {
const invalidNum = sanitization.sanitizeNumber("not-a-number"); // Throws McpError
} catch (e) {
// logger.error("Number sanitization failed", e as Error);
}
```
## 5. General Best Practices
- **Imports:** Use `.js` extension for local module imports (e.g., `import { logger } from './logger.js';`). Prefer importing from barrel files (`index.ts` or `index.js`) where available (e.g. `import { logger } from '../index.js'`).
- **Async/Await:** Use `async/await` for all asynchronous operations.
- **Type Safety:** Leverage TypeScript. Define clear interfaces and types. Avoid `any` where possible.
- **Configuration:** Access application configuration via the `config` object from `src/config/index.js`.
- **Immutability:** Prefer immutable data structures where practical.
- **Testing:** Write unit tests for new logic, especially for utilities and services.
- **Code Comments:** Document complex logic, non-obvious decisions, and public APIs with JSDoc.
## 6. Key Repository Structure and Conventions
This section outlines other important directories and their roles within the Atlas MCP Server.
### 6.0. Application Entry Point (`src/index.ts`)
- **Purpose:** The main entry point for the Atlas MCP Server application. It orchestrates the startup sequence, including logger initialization, configuration loading, server transport initialization, and setting up graceful shutdown handlers.
- **Key Responsibilities:**
- Initializes the `logger` (see Section 2).
- Loads application `config` and `environment` from `src/config/index.js` (see Section 6.1).
- Calls `initializeAndStartServer` from `src/mcp/server.js` to start the MCP server.
- Manages a global `serverInstance` for `stdio` transport to facilitate graceful shutdown.
- Implements a `shutdown` function that handles `SIGTERM`, `SIGINT`, `uncaughtException`, and `unhandledRejection` signals/events to ensure services like Neo4j connections (via `closeNeo4jConnection`) are closed properly before exiting.
- Uses `requestContextService` for creating context for startup and shutdown operations.
- **Convention:** This file is critical for understanding the application lifecycle. Modifications here typically relate to fundamental startup or shutdown procedures.
### 6.1. Configuration (`src/config/index.ts`)
- **Purpose:** Centralized application configuration. This file likely exports a configuration object (`config`) and an environment identifier (`environment`), loaded from environment variables, `.env` files, or default values.
- **Usage:** Import the `config` and `environment` objects from this module to access configuration settings and environment context throughout the application.
```typescript
import { config, environment } from './src/config/index.js';
const apiKey = config.someApiServiceKey;
const port = config.serverPort;
const currentEnv = environment; // e.g., 'development', 'production'
```
- **Convention:** Avoid hardcoding configuration values. Define them in `.env` (for local development, gitignored) or environment variables for production, and access them via the exported `config` object.
### 6.2. MCP Layer (`src/mcp/`)
This directory contains the core logic for the Model Context Protocol (MCP) server.
#### 6.2.1. Resources (`src/mcp/resources/`)
- **Purpose:** Defines and manages the data entities (like knowledge, projects, tasks) that the MCP server exposes. Each subdirectory (e.g., `knowledge/`, `projects/`, `tasks/`) typically handles CRUD-like operations or access logic for a specific resource type.
- `index.ts` in this directory aggregates and exports all resource handlers.
- `types.ts` defines common types or interfaces used across different resources.
- **Convention:** When adding a new data entity to be managed by MCP, create a new subdirectory here with its corresponding logic (e.g., `*ResourceName*Resources.ts`) and types. Ensure it's exported via `src/mcp/resources/index.ts`.
#### 6.2.2. Tools (`src/mcp/tools/`)
- **Purpose:** Implements the specific tools that the MCP server offers (e.g., `atlas_deep_research`, `atlas_knowledge_add`). Each tool is usually in its own subdirectory.
- **Structure per tool:**
- `*ToolName*.ts`: Contains the main execution logic for the tool (e.g., `addKnowledge.ts`).
- `index.ts`: Exports the tool's definition and handler.
- `responseFormat.ts`: Defines the structure of the tool's successful response.
- `types.ts`: Defines input/argument types and other specific types for the tool.
- **Convention:** New tools should follow this established directory structure. Ensure tools perform input validation (using types from `types.ts`) and return responses as defined in `responseFormat.ts` or an `McpError`.
#### 6.2.3. Transports (`src/mcp/transports/`)
- **Purpose:** Handles the communication protocols for the MCP server, such as HTTP (`httpTransport.ts`) and standard I/O (`stdioTransport.ts`).
- `authentication/authMiddleware.ts`: Handles JWT Bearer token authentication for HTTP transport. Verifies tokens, extracts `clientId` and `scopes` into an `AuthInfo` object (conforming to MCP SDK) on `req.auth`, and integrates `requestContextService` for logging.
- **Convention:** Modifications to how the server receives requests or sends responses, or changes to authentication mechanisms, would be made here.
#### 6.2.4. Server (`src/mcp/server.ts`)
- **Purpose:** The main entry point or orchestrator for the MCP server logic. It likely initializes transports, registers tools and resources, and starts listening for incoming requests. This is called by `src/index.ts`.
- **Convention:** This file ties together the different parts of the `src/mcp/` layer.
### 6.3. Services (`src/services/neo4j/`)
- **Purpose:** Encapsulates all interactions with the Neo4j graph database. Logic is typically separated into service files based on the data entity they manage (e.g., `knowledgeService.ts`, `projectService.ts`, `taskService.ts`, `searchService.ts`) or specific functionalities (`backupRestoreService.ts`).
- `driver.ts`: Manages the Neo4j driver instance and database connection.
- `events.ts`: May define or handle domain events related to database interactions.
- `helpers.ts`, `utils.ts`: Contain utility functions specific to Neo4j interactions.
- `types.ts`: Defines types and interfaces relevant to Neo4j data models and query results.
- `index.ts`: Barrel file that exports key services and utilities for easy import.
- **Convention:** All database operations MUST go through these services. Avoid direct database calls from other parts of the application (e.g., from MCP tools). Services should handle query building, execution, and mapping results to application-defined types.
### 6.4. Core Types (`src/types/`)
- **Purpose:** Defines global and core TypeScript types and interfaces used across the application.
- `errors.ts`: (Covered in Section 3) Defines `McpError` and various error code enums.
- `mcp.ts`: Likely defines core MCP structures, such as the shape of MCP requests and responses, content types, etc.
- `tool.ts`: Probably defines the generic structure or interface for an MCP tool definition, including its input schema and handler function signature.
- **Convention:** When defining types that are broadly applicable or fundamental to the MCP framework or tool interactions, place them here.
### 6.5. Utility Scripts (`scripts/`)
- **Purpose:** Contains various TypeScript-based utility scripts for development, maintenance, and operational tasks.
- Examples:
- `clean.ts`: Possibly for cleaning build artifacts or temporary files.
- `db-backup.ts`, `db-import.ts`: For backing up and restoring the Neo4j database.
- `fetch-openapi-spec.ts`: For fetching or updating OpenAPI specifications, potentially for tool schema generation or documentation.
- `make-executable.ts`: Might be used to set execute permissions on scripts.
- `tree.ts`: Generates the `docs/tree.md` file.
- **Convention:** Scripts should be well-documented regarding their purpose and usage. Use `npm run script-name` (after defining in `package.json` scripts section) or `npx tsx scripts/scriptName.ts` to execute them.
### 6.6. Core Utilities (`src/utils/`)
This directory houses various utility modules, organized by concern. Sections 1, 2, 3, and 4 of this document cover `requestContext.ts`, `logger.ts`, `errorHandler.ts` (all in `src/utils/internal/`) and `sanitization.ts` (in `src/utils/security/`) respectively. Other key subdirectories include:
#### 6.6.1. Metrics (`src/utils/metrics/`)
- **Purpose:** Provides utilities related to application metrics and monitoring.
- `tokenCounter.ts`: Implements functionality for counting or tracking token usage, which can be essential for services interacting with token-based APIs (e.g., LLMs).
- `index.ts`: Barrel file exporting metrics utilities.
#### 6.6.2. Parsing (`src/utils/parsing/`)
- **Purpose:** Contains modules for parsing various data formats safely and effectively.
- `dateParser.ts`: Utilities for parsing and handling date strings or objects.
- `jsonParser.ts`: Provides safe JSON parsing capabilities. Note: `sanitization.sanitizeJson` (Section 4) also offers JSON parsing with validation and should be preferred for external inputs. This module might offer alternative or supplementary JSON processing tools.
- `index.ts`: Barrel file exporting parsing utilities.
#### 6.6.3. Security (`src/utils/security/`)
- **Purpose:** Centralizes security-related utilities beyond what's covered in Section 4 (Sanitization).
- `sanitization.ts`: (Covered in detail in Section 4).
- `idGenerator.ts`: Provides functions for generating various types of unique identifiers used within the application (e.g., UUIDs).
- `rateLimiter.ts`: Implements mechanisms for rate limiting requests or operations to protect system resources and prevent abuse.
- `index.ts`: Barrel file exporting security utilities.
#### 6.6.4. General Utilities (`src/utils/index.ts`)
- The `src/utils/index.ts` file serves as the primary barrel file for all utilities, re-exporting modules from `internal`, `metrics`, `parsing`, and `security` subdirectories, providing a single point of import for most utility functions.
```typescript
// Example: Importing multiple utils
import { logger, requestContextService, sanitization, idGenerator } from './src/utils/index.js';
```
---
*This cheat sheet is a living document. Please contribute improvements and keep it up-to-date.*
```
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
```markdown
# Atlas MCP Server Backup Example
This repository contains the `backup-example/` directory which demonstrates how Atlas MCP Server's backup functionality works.
## Overview
The backup feature allows you to export your project's knowledge and tasks into JSON format for external storage, sharing, or analysis. The example project in this directory shows what the output from running `npm run db:backup` looks like.
## Contents
The `backup-example/` directory contains:
- Sample task data exported in JSON format
- Example of how knowledge and context are stored
- Format of the backup files generated by the system
## How to Use the Backup Feature
To backup your own Atlas MCP Server data:
1. Navigate to your Atlas MCP Server project root directory
2. Run `npm run db:backup` in your terminal
3. Your data will be exported to a format similar to the examples in this directory
## Example Structure
The backup files include:
- `tasks.json`: Contains all project tasks with their metadata, status, and requirements
- Knowledge data with context and relationships
- Project configuration information
## Purpose
This example is provided to:
- Help users understand the structure of backed-up data
- Demonstrate the completeness of the backup feature
- Provide a reference for data restoration or migration
## Notes
The backup feature is designed for data portability and disaster recovery. Regular backups are recommended to ensure your project data is protected.
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# ATLAS: Task Management System
[](https://www.typescriptlang.org/)
[](https://modelcontextprotocol.io/)
[](https://github.com/cyanheads/atlas-mcp-server/releases)
[](https://opensource.org/licenses/Apache-2.0)
[]()
[](https://github.com/cyanheads/atlas-mcp-server)
ATLAS (Adaptive Task & Logic Automation System) is a project, knowledge, and task management system for LLM Agents.
Built on a 3-node architecture:
```
+-------------------------------------------+
| PROJECT |
|-------------------------------------------|
| id: string |
| name: string |
| description: string |
| status: string |
| urls?: Array<{title: string, url: string}>|
| completionRequirements: string |
| outputFormat: string |
| taskType: string |
| createdAt: string |
| updatedAt: string |
+----------------+--------------------------+
| |
| |
v v
+----------------------------------+ +----------------------------------+
| TASK | | KNOWLEDGE |
|----------------------------------| |----------------------------------|
| id: string | | id: string |
| projectId: string | | projectId: string |
| title: string | | text: string |
| description: string | | tags?: string[] |
| priority: string | | domain: string |
| status: string | | citations?: string[] |
| assignedTo?: string | | createdAt: string |
| urls?: Array<{title: string, | | |
| url: string}> | | updatedAt: string |
| tags?: string[] | | |
| completionRequirements: string | | |
| outputFormat: string | | |
| taskType: string | | |
| createdAt: string | | |
| updatedAt: string | | |
+----------------------------------+ +----------------------------------+
```
Implemented as a Model Context Protocol (MCP) server, ATLAS allows LLM agents to interact with a project management database, enabling them to manage projects, tasks, and knowledge items.
> **Important Version Note**: [Version 1.5.4](https://github.com/cyanheads/atlas-mcp-server/releases/tag/v1.5.4) is the last version that uses SQLite as the database. Version 2.0 and onwards has been completely rewritten to use Neo4j, which requires either:
>
> - Self-hosting using Docker (docker-compose included in repository)
> - Using Neo4j AuraDB cloud service: https://neo4j.com/product/auradb/
>
> Version 2.5.0 introduces a new 3-node system (Projects, Tasks, Knowledge) that replaces the previous structure.
## Table of Contents
- [Overview](#overview)
- [Features](#features)
- [Installation](#installation)
- [Running the Server](#running-the-server)
- [Web UI (Experimental)](#web-ui-experimental)
- [Configuration](#configuration)
- [Project Structure](#project-structure)
- [Tools](#tools)
- [Resources](#resources)
- [Database Backup and Restore](#database-backup-and-restore)
- [Examples](#examples)
- [License](#license)
## Overview
ATLAS implements the Model Context Protocol (MCP), enabling standardized communication between LLMs and external systems through:
- **Clients**: Claude Desktop, IDEs, and other MCP-compatible clients
- **Servers**: Tools and resources for project, task, and knowledge management
- **LLM Agents**: AI models that leverage the server's management capabilities
### System Integration
The Atlas Platform integrates these components into a cohesive system:
- **Project-Task Relationship**: Projects contain tasks that represent actionable steps needed to achieve project goals. Tasks inherit context from their parent project while providing granular tracking of individual work items.
- **Knowledge Integration**: Both projects and tasks can be enriched with knowledge items, providing team members with necessary information and context.
- **Dependency Management**: Both projects and tasks support dependency relationships, allowing for complex workflows with prerequisites and sequential execution requirements.
- **Unified Search**: The platform provides cross-entity search capabilities, allowing users to find relevant projects, tasks, or knowledge based on various criteria.
## Features
| Feature Area | Key Capabilities |
| :----------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Project Management** | - Comprehensive Tracking: Manage project metadata, statuses, and rich content (notes, links, etc.) with built-in support for bulk operations.<br />- Dependency & Relationship Handling: Automatically validate and track inter-project dependencies. |
| **Task Management** | - Task Lifecycle Management: Create, track, and update tasks through their entire lifecycle.<br />- Prioritization & Categorization: Assign priority levels and categorize tasks with tags for better organization.<br />- Dependency Tracking: Establish task dependencies to create structured workflows. |
| **Knowledge Management** | - Structured Knowledge Repository: Maintain a searchable repository of project-related information.<br />- Domain Categorization: Organize knowledge by domain and tags for easy retrieval.<br />- Citation Support: Track sources and references for knowledge items. |
| **Graph Database Integration** | - Native Relationship Management: Leverage Neo4j's ACID-compliant transactions and optimized queries for robust data integrity.<br />- Advanced Search & Scalability: Perform property-based searches with fuzzy matching and wildcards while maintaining high performance. |
| **Unified Search** | - Cross-Entity Search: Find relevant projects, tasks, or knowledge based on content, metadata, or relationships.<br />- Flexible Query Options: Support for case-insensitive, fuzzy, and advanced filtering options. |
## Installation
1. **Clone the repository:**
```bash
git clone https://github.com/cyanheads/atlas-mcp-server.git
cd atlas-mcp-server
```
2. **Install dependencies:**
```bash
npm install
```
3. **Configure Neo4j:**
Ensure you have a Neo4j instance running and accessible. You can start one using the provided Docker configuration:
```bash
docker-compose up -d
```
Update your `.env` file with the Neo4j connection details (see [Configuration](#configuration)).
4. **Build the project:**
```bash
npm run build
```
## Running the Server
Most MCP Clients run the server automatically, but you can also run it manually for testing or development purposes using the following commands.
ATLAS MCP Server supports multiple transport mechanisms for communication:
- **Standard I/O (stdio):** This is the default mode and is typically used for direct integration with local MCP clients (like IDE extensions).
```bash
npm run start:stdio
```
This uses the `MCP_TRANSPORT_TYPE=stdio` setting.
- **Streamable HTTP:** This mode allows the server to listen for MCP requests over HTTP, suitable for remote clients or web-based integrations.
```bash
npm run start:http
```
This uses the `MCP_TRANSPORT_TYPE=http` setting. The server will listen on the host and port defined in your `.env` file (e.g., `MCP_HTTP_HOST` and `MCP_HTTP_PORT`, defaulting to `127.0.0.1:3010`). Ensure your firewall allows connections if accessing remotely.
## Web UI (Experimental)
A basic Web UI is available for viewing Project, Task, & Knowledge details.
- **Opening the UI**:
- To open the UI directly in your browser, run the following command in your terminal:
```bash
npm run webui
```
- **Functionality**:
- You can see an example screenshot of the Web UI [here](./examples/webui-example.png).
## Configuration
### Environment Variables
Environment variables should be set in the client config in your MCP Client, or in a `.env` file in the project root for local development.
```bash
# Neo4j Configuration
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=password2
# Application Configuration
MCP_LOG_LEVEL=debug # Minimum logging level. Options: emerg, alert, crit, error, warning, notice, info, debug. Default: "debug".
LOGS_DIR=./logs # Directory for log files. Default: "./logs" in project root.
NODE_ENV=development # 'development' or 'production'. Default: "development".
# MCP Transport Configuration
MCP_TRANSPORT_TYPE=stdio # 'stdio' or 'http'. Default: "stdio".
MCP_HTTP_HOST=127.0.0.1 # Host for HTTP transport. Default: "127.0.0.1".
MCP_HTTP_PORT=3010 # Port for HTTP transport. Default: 3010.
# MCP_ALLOWED_ORIGINS=http://localhost:someport,https://your-client.com # Optional: Comma-separated list of allowed origins for HTTP CORS.
# MCP Security Configuration
# MCP_AUTH_SECRET_KEY=your_very_long_and_secure_secret_key_min_32_chars # Optional: Secret key (min 32 chars) for JWT authentication if HTTP transport is used. CRITICAL for production. *Note: Production environment use has not been tested yet.*
MCP_RATE_LIMIT_WINDOW_MS=60000 # Rate limit window in milliseconds. Default: 60000 (1 minute).
MCP_RATE_LIMIT_MAX_REQUESTS=100 # Max requests per window per IP for HTTP transport. Default: 100.
# Database Backup Configuration
BACKUP_MAX_COUNT=10 # Maximum number of backup sets to keep. Default: 10.
BACKUP_FILE_DIR=./atlas-backups # Directory where backup files will be stored (relative to project root). Default: "./atlas-backups".
```
Refer to `src/config/index.ts` for all available environment variables, their descriptions, and default values.
### MCP Client Settings
How you configure your MCP client depends on the client itself and the chosen transport type. An `mcp.json` file in the project root can be used by some clients (like `mcp-inspector`) to define server configurations; update as needed.
**For Stdio Transport (Example Configuration):**
```json
{
"mcpServers": {
"atlas-mcp-server-stdio": {
"command": "node",
"args": ["/full/path/to/atlas-mcp-server/dist/index.js"],
"env": {
"NEO4J_URI": "bolt://localhost:7687",
"NEO4J_USER": "neo4j",
"NEO4J_PASSWORD": "password2",
"MCP_LOG_LEVEL": "info",
"NODE_ENV": "development",
"MCP_TRANSPORT_TYPE": "stdio"
}
}
}
}
```
**For Streamable HTTP (Example Configuration):**
If your client supports connecting to an MCP server via Streamable HTTP, you provide the server's endpoint (e.g., `http://localhost:3010/mcp`) in your client configuration.
```json
{
"mcpServers": {
"atlas-mcp-server-http": {
"command": "node",
"args": ["/full/path/to/atlas-mcp-server/dist/index.js"],
"env": {
"NEO4J_URI": "bolt://localhost:7687",
"NEO4J_USER": "neo4j",
"NEO4J_PASSWORD": "password2",
"MCP_LOG_LEVEL": "info",
"NODE_ENV": "development",
"MCP_TRANSPORT_TYPE": "http",
"MCP_HTTP_PORT": "3010",
"MCP_HTTP_HOST": "127.0.0.1"
// "MCP_AUTH_SECRET_KEY": "your-secure-token" // If authentication is enabled on the server
}
}
}
}
```
**Note:** Always use absolute paths for `args` when configuring client commands if the server is not in the client's immediate working directory. The `MCP_AUTH_SECRET_KEY` in the client's `env` block is illustrative; actual token handling for client-to-server communication would depend on the client's capabilities and the server's authentication mechanism (e.g., sending a JWT in an `Authorization` header).
## Project Structure
The codebase follows a modular structure:
```
src/
├── config/ # Configuration management (index.ts)
├── index.ts # Main server entry point
├── mcp/ # MCP server implementation (server.ts)
│ ├── resources/ # MCP resource handlers (index.ts, types.ts, knowledge/, projects/, tasks/)
│ └── tools/ # MCP tool handlers (individual tool directories)
├── services/ # Core application services
│ └── neo4j/ # Neo4j database services (index.ts, driver.ts, backupRestoreService.ts, etc.)
├── types/ # Shared TypeScript type definitions (errors.ts, mcp.ts, tool.ts)
└── utils/ # Utility functions and internal services (e.g., logger, errorHandler, sanitization)
```
## Tools
ATLAS provides a comprehensive suite of tools for project, task, and knowledge management, callable via the Model Context Protocol.
### Project Operations
| Tool Name | Description | Key Arguments |
| :--------------------- | :--------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `atlas_project_create` | Creates new projects (single/bulk). | `mode` ('single'/'bulk'), `id` (optional client-generated ID for single mode), project details (`name`, `description`, `status`, `urls`, `completionRequirements`, `dependencies`, `outputFormat`, `taskType`). For bulk mode, use `projects` (array of project objects). `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
| `atlas_project_list` | Lists projects (all/details). | `mode` ('all'/'details', default: 'all'), `id` (for details mode), filters (`status`, `taskType`), pagination (`page`, `limit`), includes (`includeKnowledge`, `includeTasks`), `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
| `atlas_project_update` | Updates existing projects (single/bulk). | `mode` ('single'/'bulk'), `id` (for single mode), `updates` object. For bulk mode, use `projects` (array of objects, each with `id` and `updates`). `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
| `atlas_project_delete` | Deletes projects (single/bulk). | `mode` ('single'/'bulk'), `id` (for single mode) or `projectIds` (array for bulk mode). `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
### Task Operations
| Tool Name | Description | Key Arguments |
| :------------------ | :------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `atlas_task_create` | Creates new tasks (single/bulk). | `mode` ('single'/'bulk'), `id` (optional client-generated ID), `projectId`, task details (`title`, `description`, `priority`, `status`, `assignedTo`, `urls`, `tags`, `completionRequirements`, `dependencies`, `outputFormat`, `taskType`). For bulk mode, use `tasks` (array of task objects). `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
| `atlas_task_update` | Updates existing tasks (single/bulk). | `mode` ('single'/'bulk'), `id` (for single mode), `updates` object. For bulk mode, use `tasks` (array of objects, each with `id` and `updates`). `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
| `atlas_task_delete` | Deletes tasks (single/bulk). | `mode` ('single'/'bulk'), `id` (for single mode) or `taskIds` (array for bulk mode). `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
| `atlas_task_list` | Lists tasks for a specific project. | `projectId` (required), filters (`status`, `assignedTo`, `priority`, `tags`, `taskType`), sorting (`sortBy`, `sortDirection`), pagination (`page`, `limit`), `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
### Knowledge Operations
| Tool Name | Description | Key Arguments |
| :----------------------- | :-------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `atlas_knowledge_add` | Adds new knowledge items (single/bulk). | `mode` ('single'/'bulk'), `id` (optional client-generated ID), `projectId`, knowledge details (`text`, `tags`, `domain`, `citations`). For bulk mode, use `knowledge` (array of knowledge objects). `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
| `atlas_knowledge_delete` | Deletes knowledge items (single/bulk). | `mode` ('single'/'bulk'), `id` (for single mode) or `knowledgeIds` (array for bulk mode). `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
| `atlas_knowledge_list` | Lists knowledge items for a specific project. | `projectId` (required), filters (`tags`, `domain`, `search`), pagination (`page`, `limit`), `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
### Search Operations
| Tool Name | Description | Key Arguments |
| :--------------------- | :--------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `atlas_unified_search` | Performs unified search across entities. | `value` (search term, required), `property` (optional: if specified, performs regex search on this property; if omitted, performs full-text search), filters (`entityTypes`, `taskType`, `assignedToUserId`), options (`caseInsensitive` (default: true, for regex), `fuzzy` (default: false, for regex 'contains' or full-text Lucene fuzzy)), pagination (`page`, `limit`), `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
### Research Operations
| Tool Name | Description | Key Arguments |
| :-------------------- | :------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `atlas_deep_research` | Initiates a structured deep research process by creating a hierarchical plan within the Atlas knowledge base. | `projectId` (required), `researchTopic` (required), `researchGoal` (required), `scopeDefinition` (optional), `subTopics` (required array of objects, each with `question` (required), `initialSearchQueries` (optional array), `nodeId` (optional), `priority` (optional), `assignedTo` (optional), `initialStatus` (optional, default: 'todo')), `researchDomain` (optional), `initialTags` (optional), `planNodeId` (optional), `createTasks` (optional, default: true), `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
### Database Operations
| Tool Name | Description | Key Arguments |
| :--------------------- | :-------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------- |
| `atlas_database_clean` | **Destructive:** Completely resets the database, removing all projects, tasks, and knowledge. | `acknowledgement` (must be set to `true` to confirm, required), `responseFormat` ('formatted'/'json', optional, default: 'formatted'). |
## Resources
ATLAS exposes project, task, and knowledge data through standard MCP resource endpoints.
### Direct Resources
| Resource Name | Description |
| :------------------ | :--------------------------------------------------------------------------------------- |
| `atlas://projects` | List of all projects in the Atlas platform with pagination support. |
| `atlas://tasks` | List of all tasks in the Atlas platform with pagination and filtering support. |
| `atlas://knowledge` | List of all knowledge items in the Atlas platform with pagination and filtering support. |
### Resource Templates
| Resource Name | Description |
| :--------------------------------------- | :--------------------------------------------------------------------------- |
| `atlas://projects/{projectId}` | Retrieves a single project by its unique identifier (`projectId`). |
| `atlas://tasks/{taskId}` | Retrieves a single task by its unique identifier (`taskId`). |
| `atlas://projects/{projectId}/tasks` | Retrieves all tasks belonging to a specific project (`projectId`). |
| `atlas://knowledge/{knowledgeId}` | Retrieves a single knowledge item by its unique identifier (`knowledgeId`). |
| `atlas://projects/{projectId}/knowledge` | Retrieves all knowledge items belonging to a specific project (`projectId`). |
## Database Backup and Restore
ATLAS provides functionality to back up and restore the Neo4j database content. The core logic resides in `src/services/neo4j/backupRestoreService.ts`.
### Backup Process
- **Mechanism**: The backup process exports all `Project`, `Task`, and `Knowledge` nodes, along with their relationships, into separate JSON files. A `full-export.json` containing all data is also created.
- **Output**: Each backup creates a timestamped directory (e.g., `atlas-backup-YYYYMMDDHHMMSS`) within the configured backup path (default: `./atlas-backups/`). This directory contains `projects.json`, `tasks.json`, `knowledge.json`, `relationships.json`, and `full-export.json`.
- **Manual Backup**: You can trigger a manual backup using the provided script:
```bash
npm run db:backup
```
This command executes `src/services/neo4j/backupRestoreService/scripts/db-backup.ts`, which calls the `exportDatabase` function.
### Restore Process
- **Mechanism**: The restore process first completely clears the existing Neo4j database. Then, it imports nodes and relationships from the JSON files located in the specified backup directory. It prioritizes `full-export.json` if available.
- **Warning**: Restoring from a backup is a destructive operation. **It will overwrite all current data in your Neo4j database.**
- **Manual Restore**: To restore the database from a backup directory, use the import script:
```bash
npm run db:import <path_to_backup_directory>
```
Replace `<path_to_backup_directory>` with the actual path to the backup folder (e.g., `./atlas-backups/atlas-backup-20250326120000`). This command executes `src/services/neo4j/backupRestoreService/scripts/db-import.ts`, which calls the `importDatabase` function.
- **Relationship Handling**: The import process attempts to recreate relationships based on the `id` properties stored within the nodes during export. Ensure your nodes have consistent `id` properties for relationships to be restored correctly.
## Examples
The `examples/` directory contains practical examples demonstrating various features of the ATLAS MCP Server.
- **Backup Example**: Located in `examples/backup-example/`, this shows the structure and format of the JSON files generated by the `npm run db:backup` command. See the [Examples README](./examples/README.md) for more details.
- **Deep Research Example**: Located in `examples/deep-research-example/`, this demonstrates the output and structure generated by the `atlas_deep_research` tool. It includes a markdown file (`covington_community_grant_research.md`) summarizing the research plan and a JSON file (`full-export.json`) containing the raw data exported from the database after the research plan was created. See the [Examples README](./examples/README.md) for more details.
## License
Apache License 2.0
---
<div align="center">
Built with the Model Context Protocol
</div>
```
--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------
```markdown
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Essential Development Commands
### Build and Development
- `npm run build` - Build the TypeScript project and make executable
- `npm run dev` - Watch mode for TypeScript compilation
- `npm run rebuild` - Clean and rebuild project completely
- `npm run format` - Format code with Prettier
### Database Operations
- `npm run db:backup` - Create database backup with timestamped directory
- `npm run db:import <backup_path>` - Restore database from backup (destructive)
- `docker-compose up -d` - Start Neo4j database
- `docker-compose down` - Stop Neo4j database
### Running the Server
- `npm run start:stdio` - Run with stdio transport (default for MCP clients)
- `npm run start:http` - Run with HTTP transport on localhost:3010
- `npm run inspector` - Run MCP inspector for debugging
### Testing and Quality
- `npm run webui` - Open basic web UI for viewing data
- `npm run tree` - Generate project structure documentation
## Core Architecture
ATLAS is an MCP (Model Context Protocol) server with a three-tier Neo4j-backed architecture:
**Transport Layer** (`src/mcp/transports/`):
- `stdioTransport.ts` - Direct stdio communication (default)
- `httpTransport.ts` - HTTP server with authentication/rate limiting
**MCP Layer** (`src/mcp/`):
- `server.ts` - Main MCP server setup, tool/resource registration
- `tools/` - MCP tool implementations (15 total tools)
- `resources/` - MCP resource handlers for direct data access
**Data Layer** (`src/services/neo4j/`):
- Core services: `projectService.ts`, `taskService.ts`, `knowledgeService.ts`
- `searchService.ts` - Unified search across all entities
- `backupRestoreService.ts` - Database backup/restore operations
**Three-Tier Data Model**:
```
PROJECT (top-level containers)
├── TASK (actionable items within projects)
├── KNOWLEDGE (information/context for projects)
└── DEPENDENCIES (relationships between entities)
```
## Configuration
Environment variables are validated via Zod schema in `src/config/index.ts`. Key settings:
**Database**: `NEO4J_URI`, `NEO4J_USER`, `NEO4J_PASSWORD`
**Transport**: `MCP_TRANSPORT_TYPE` (stdio/http), `MCP_HTTP_PORT` (3010)
**Logging**: `MCP_LOG_LEVEL` (debug), `LOGS_DIR` (./logs)
**Backup**: `BACKUP_FILE_DIR` (./atlas-backups), `BACKUP_MAX_COUNT` (10)
## Key Implementation Notes
- All tools support both single and bulk operations via `mode` parameter
- Comprehensive input validation using Zod schemas per tool
- Request context tracking for operations (`src/utils/internal/requestContext.ts`)
- Structured logging with Winston (`src/utils/internal/logger.ts`)
- Backup/restore creates timestamped directories with JSON exports
- Rate limiting and authentication available for HTTP transport
- LLM provider integration available via OpenRouter (`src/services/llm-providers/`)
## Database Schema
Neo4j constraints and indexes are auto-created on startup. Core node types:
- `Project` nodes with `id` property (unique constraint)
- `Task` nodes with `id` property, linked to projects via `BELONGS_TO`
- `Knowledge` nodes with `id` property, linked to projects via `BELONGS_TO`
- `DEPENDS_ON` relationships for dependency tracking
## Testing & Development
No formal test framework detected. Use:
- `npm run inspector` for MCP protocol testing
- Manual testing via stdio/HTTP transports
- Database backup/restore for data safety during development
- Web UI for visual data verification
```
--------------------------------------------------------------------------------
/tsconfig.typedoc.json:
--------------------------------------------------------------------------------
```json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"rootDir": "."
},
"include": ["src/**/*", "scripts/**/*.ts"]
// The 'exclude' is also inherited.
}
```
--------------------------------------------------------------------------------
/src/utils/metrics/index.ts:
--------------------------------------------------------------------------------
```typescript
/**
* @fileoverview Barrel file for metrics-related utility modules.
* This file re-exports utilities for collecting and processing metrics,
* such as token counting.
* @module src/utils/metrics
*/
export * from "./tokenCounter.js";
```
--------------------------------------------------------------------------------
/src/utils/parsing/index.ts:
--------------------------------------------------------------------------------
```typescript
/**
* @fileoverview Barrel file for parsing utility modules.
* This file re-exports utilities related to parsing various data formats,
* such as JSON and dates.
* @module src/utils/parsing
*/
export * from "./dateParser.js";
export * from "./jsonParser.js";
```
--------------------------------------------------------------------------------
/src/utils/security/index.ts:
--------------------------------------------------------------------------------
```typescript
/**
* @fileoverview Barrel file for security-related utility modules.
* This file re-exports utilities for input sanitization, rate limiting,
* and ID generation.
* @module src/utils/security
*/
export * from "./idGenerator.js";
export * from "./rateLimiter.js";
export * from "./sanitization.js";
```
--------------------------------------------------------------------------------
/src/utils/internal/index.ts:
--------------------------------------------------------------------------------
```typescript
/**
* @fileoverview Barrel file for internal utility modules.
* This file re-exports core internal utilities related to error handling,
* logging, and request context management.
* @module src/utils/internal
*/
export * from "./errorHandler.js";
export * from "./logger.js";
export * from "./requestContext.js";
```
--------------------------------------------------------------------------------
/typedoc.json:
--------------------------------------------------------------------------------
```json
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": ["src", "scripts"],
"entryPointStrategy": "expand",
"out": "docs/api",
"readme": "README.md",
"name": "atlas-mcp-server API Documentation",
"includeVersion": true,
"excludePrivate": true,
"excludeProtected": true,
"excludeInternal": true,
"theme": "default"
}
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
```
--------------------------------------------------------------------------------
/repomix.config.json:
--------------------------------------------------------------------------------
```json
{
"output": {
"filePath": "repomix-output.xml",
"style": "xml",
"removeComments": false,
"removeEmptyLines": false,
"topFilesLength": 5,
"showLineNumbers": false,
"copyToClipboard": false
},
"include": [],
"ignore": {
"useGitignore": true,
"useDefaultPatterns": true,
"customPatterns": [".clinerules"]
},
"security": {
"enableSecurityCheck": true
}
}
```
--------------------------------------------------------------------------------
/src/services/neo4j/backupRestoreService/backupRestoreTypes.ts:
--------------------------------------------------------------------------------
```typescript
/**
* @fileoverview Defines types related to backup and restore functionality in the Neo4j service.
* @module src/services/neo4j/backupRestoreService/backupRestoreTypes
*/
/**
* Interface for the full export containing all entities and their relationships in a nested structure.
* Nodes are stored in an object keyed by their label.
*/
export interface FullExport {
nodes: { [label: string]: Record<string, any>[] };
relationships: {
startNodeId: string;
endNodeId: string;
type: string;
properties: Record<string, any>;
}[];
}
```
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
```yaml
name: Publish Package to npm
on:
push:
tags:
- "v*"
jobs:
build-and-publish:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20.x"
registry-url: "https://registry.npmjs.org"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Publish to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```
--------------------------------------------------------------------------------
/src/services/neo4j/searchService/searchTypes.ts:
--------------------------------------------------------------------------------
```typescript
/**
* @fileoverview Defines types related to search functionality in the Neo4j service.
* @module src/services/neo4j/searchService/searchTypes
*/
/**
* Type for search result items - Made generic
*/
export type SearchResultItem = {
id: string;
type: string; // Node label
entityType?: string; // Optional: Specific classification (e.g., taskType, domain)
title: string; // Best guess title (name, title, truncated text)
description?: string; // Optional: Full description or text
matchedProperty: string;
matchedValue: string; // Potentially truncated
createdAt?: string; // Optional
updatedAt?: string; // Optional
projectId?: string; // Optional
projectName?: string; // Optional
score: number;
};
```
--------------------------------------------------------------------------------
/mcp.json:
--------------------------------------------------------------------------------
```json
{
"mcpServers": {
"atlas-mcp-server": {
"command": "node",
"args": ["dist/index.js"],
"env": {
"NEO4J_URI": "bolt://localhost:7687",
"NEO4J_USER": "neo4j",
"NEO4J_PASSWORD": "password2",
"LOG_LEVEL": "info",
"NODE_ENV": "development",
"MCP_TRANSPORT_TYPE": "http",
"MCP_HTTP_HOST": "127.0.0.1",
"MCP_HTTP_PORT": "3010",
"MCP_ALLOWED_ORIGINS": "",
"MCP_AUTH_SECRET_KEY": "your-super-secret-key-min-32-chars",
"MCP_RATE_LIMIT_WINDOW_MS": "60000",
"MCP_RATE_LIMIT_MAX_REQUESTS": "100",
"BACKUP_MAX_COUNT": "10",
"BACKUP_FILE_DIR": "./atlas-backups",
"LOGS_DIR": "./logs"
}
}
}
}
```
--------------------------------------------------------------------------------
/src/mcp/resources/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerProjectResources } from "./projects/projectResources.js";
import { registerTaskResources } from "./tasks/taskResources.js";
import { registerKnowledgeResources } from "./knowledge/knowledgeResources.js";
/**
* Register all Atlas MCP resources
*
* This function registers all resources available in the Atlas MCP server:
* - Projects
* - Tasks
* - Knowledge
*
* @param server The MCP server instance
*/
export function registerMcpResources(server: McpServer) {
// Register project resources
registerProjectResources(server);
// Register task resources
registerTaskResources(server);
// Register knowledge resources
registerKnowledgeResources(server);
}
```
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
```typescript
/**
* @fileoverview Barrel file for the utils module.
* This file re-exports all utilities from their categorized subdirectories,
* providing a single entry point for accessing utility functions.
* @module src/utils
*/
// Re-export all utilities from their categorized subdirectories
export * from "./internal/index.js";
export * from "./metrics/index.js";
export * from "./parsing/index.js";
export * from "./security/index.js";
// It's good practice to have index.ts files in each subdirectory
// that export the contents of that directory.
// Assuming those will be created or already exist.
// If not, this might need adjustment to export specific files, e.g.:
// export * from './internal/errorHandler.js';
// export * from './internal/logger.js';
// ... etc.
```
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
```yaml
# These are supported funding model platforms
github: cyanheads
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: cyanheads
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
```
--------------------------------------------------------------------------------
/src/webui/logic/config.js:
--------------------------------------------------------------------------------
```javascript
/**
* @fileoverview Application configuration constants.
* @module src/webui/logic/config
*/
/**
* Application configuration settings.
* @type {object}
* @property {string} NEO4J_URI - The URI for the Neo4j database.
* @property {string} NEO4J_USER - The username for Neo4j authentication.
* @property {string} NEO4J_PASSWORD - The password for Neo4j authentication.
* @property {string} DEFAULT_THEME - The default theme ('light' or 'dark').
* @property {string} MERMAID_THEME_LIGHT - Mermaid theme for light mode.
* @property {string} MERMAID_THEME_DARK - Mermaid theme for dark mode.
*/
export const config = {
NEO4J_URI: window.NEO4J_URI || "bolt://localhost:7687",
NEO4J_USER: window.NEO4J_USER || "neo4j",
NEO4J_PASSWORD: window.NEO4J_PASSWORD || "password2",
DEFAULT_THEME: "light",
MERMAID_THEME_LIGHT: "default",
MERMAID_THEME_DARK: "dark",
};
```
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
```yaml
version: '3'
services:
neo4j:
image: neo4j:5-community
container_name: neo4j-atlas-mcp-server
ports:
- "7474:7474" # HTTP Browser
- "7687:7687" # Bolt protocol
environment:
NEO4J_PLUGINS: '["apoc"]' # Install APOC plugin
NEO4J_AUTH: neo4j/password2 # Change password for production use
NEO4J_dbms_usage__report_enabled: "false"
NEO4J_server_bolt_listen__address: ":7687"
NEO4J_server_bolt_advertised__address: ":7687"
NEO4J_server_memory_heap_initial__size: "512m"
NEO4J_server_memory_heap_max__size: "1G"
NEO4J_server_jvm_additional: "-XX:+HeapDumpOnOutOfMemoryError"
NEO4J_db_logs_query_enabled: "INFO"
NEO4J_db_logs_query_threshold: "0"
NEO4J_db_transaction_timeout: "5s"
NEO4J_dbms_security_procedures_unrestricted: "apoc.*" # Allow APOC procedures
volumes:
- neo4j_data:/data
- neo4j_logs:/logs
- neo4j_import:/var/lib/neo4j/import
volumes:
neo4j_data:
neo4j_logs:
neo4j_import:
```
--------------------------------------------------------------------------------
/src/services/neo4j/backupRestoreService/index.ts:
--------------------------------------------------------------------------------
```typescript
/**
* @fileoverview Main entry point for the Neo4j Backup and Restore Service.
* This module exports the primary functions for database backup and restore operations.
* @module src/services/neo4j/backupRestoreService/index
*/
import { _exportDatabase } from "./exportLogic.js";
import { _importDatabase } from "./importLogic.js";
import { FullExport } from "./backupRestoreTypes.js";
// Re-export types
export { FullExport } from "./backupRestoreTypes.js";
/**
* Exports the current Neo4j database to a timestamped directory.
* Manages backup rotation.
* @returns {Promise<string>} Path to the backup directory.
* @throws Error if export fails.
*/
export const exportDatabase = async (): Promise<string> => {
return _exportDatabase();
};
/**
* Imports data from a specified backup directory into the Neo4j database,
* overwriting existing data.
* @param {string} backupDirInput - Path to the backup directory.
* @returns {Promise<void>}
* @throws Error if import fails or backup directory is invalid.
*/
export const importDatabase = async (backupDirInput: string): Promise<void> => {
return _importDatabase(backupDirInput);
};
```
--------------------------------------------------------------------------------
/examples/backup-example/projects.json:
--------------------------------------------------------------------------------
```json
[
{
"createdAt": "2025-03-26T18:39:53.529Z",
"taskType": "generation",
"urls": "[{\"title\":\"GitHub Repository\",\"url\":\"https://github.com/cyanheads/portfolio\"},{\"title\":\"Live Website\",\"url\":\"https://cyanheads.dev\"}]",
"completionRequirements": "The portfolio website will be considered complete when it includes all required sections (Home, About, Projects, Skills, Experience, Contact), is fully responsive, achieves a Lighthouse score of at least 90 across all categories, includes proper SEO optimization, and is deployed to production with CI/CD pipeline integration.",
"name": "Cyanheads Portfolio Website",
"description": "A modern, responsive portfolio website showcasing cyanheads' skills, projects, and professional experience as a software developer based in Seattle, WA. The site will feature a clean, intuitive design with sections for About, Projects, Skills, Experience, and Contact information. The portfolio will emphasize cyanheads' expertise in full-stack development, cloud technologies, and open source contributions.",
"id": "portfolio-main",
"outputFormat": "The final deliverable will be a fully functional website deployed to a production environment with source code hosted on GitHub. Documentation will include setup instructions, deployment procedures, and content management guidelines.",
"updatedAt": "2025-03-26T18:39:53.529Z",
"status": "active"
}
]
```
--------------------------------------------------------------------------------
/src/services/neo4j/searchService/index.ts:
--------------------------------------------------------------------------------
```typescript
/**
* @fileoverview Provides a SearchService class for unified and full-text search
* across Neo4j entities. This service acts as an orchestrator for different
* search strategies.
* @module src/services/neo4j/searchService/index
*/
import { PaginatedResult, SearchOptions } from "../types.js";
import { _fullTextSearch } from "./fullTextSearchLogic.js";
import { SearchResultItem } from "./searchTypes.js";
import { _searchUnified } from "./unifiedSearchLogic.js";
export { SearchResultItem } from "./searchTypes.js";
/**
* Service for unified and full-text search functionality across all entity types.
*/
export class SearchService {
/**
* Perform a unified search across multiple entity types (node labels).
* Searches common properties like name, title, description, text.
* Applies pagination after combining and sorting results from individual label searches.
* @param options Search options
* @returns Paginated search results
*/
static async search(
options: SearchOptions,
): Promise<PaginatedResult<SearchResultItem>> {
return _searchUnified(options);
}
/**
* Perform a full-text search using pre-configured Neo4j full-text indexes.
* @param searchValue The string to search for.
* @param options Search options, excluding those not relevant to full-text search.
* @returns Paginated search results
*/
static async fullTextSearch(
searchValue: string,
options: Omit<
SearchOptions,
"value" | "fuzzy" | "caseInsensitive" | "property" | "assignedToUserId"
> = {},
): Promise<PaginatedResult<SearchResultItem>> {
return _fullTextSearch(searchValue, options);
}
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_database_clean/types.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from "zod";
import {
McpToolResponse,
ResponseFormat,
createResponseFormatEnum,
} from "../../../types/mcp.js";
/**
* Schema for database clean operation
* This operation requires an explicit acknowledgement to prevent accidental data loss
*/
export const AtlasDatabaseCleanSchema = z
.object({
acknowledgement: z
.literal(true)
.describe(
"Explicit acknowledgement to reset the entire database (must be set to TRUE)",
),
responseFormat: createResponseFormatEnum()
.optional()
.default(ResponseFormat.FORMATTED)
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
})
.strict();
/**
* Schema shape for tool registration
*/
export const AtlasDatabaseCleanSchemaShape = {
acknowledgement: z
.literal(true)
.describe(
"Explicit acknowledgement to reset the entire database (must be set to TRUE)",
),
responseFormat: createResponseFormatEnum()
.optional()
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
} as const;
/**
* Type for database clean input (empty object)
*/
export type AtlasDatabaseCleanInput = z.infer<typeof AtlasDatabaseCleanSchema>;
/**
* Type for database clean response
*/
export interface AtlasDatabaseCleanResponse extends McpToolResponse {
success: boolean;
message: string;
timestamp: string;
}
/**
* Type for formatted database clean response
*/
export interface FormattedDatabaseCleanResponse {
success: boolean;
message: string;
timestamp: string;
// Removed optional 'details' field as the current implementation doesn't provide these counts
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_task_delete/types.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from "zod";
import {
McpToolResponse,
ResponseFormat,
createResponseFormatEnum,
} from "../../../types/mcp.js";
// Schema for individual task deletion
const SingleTaskSchema = z
.object({
mode: z.literal("single"),
id: z
.string()
.describe("Task identifier to permanently remove from the system"),
responseFormat: createResponseFormatEnum()
.optional()
.default(ResponseFormat.FORMATTED)
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
})
.describe("Remove a specific task entity by its unique identifier");
// Schema for multi-task cleanup operation
const BulkTaskSchema = z
.object({
mode: z.literal("bulk"),
taskIds: z
.array(z.string())
.min(1)
.describe(
"Collection of task identifiers to remove in a single operation",
),
responseFormat: createResponseFormatEnum()
.optional()
.default(ResponseFormat.FORMATTED)
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
})
.describe("Batch removal of multiple task entities in a single transaction");
// Schema shapes for tool registration
export const AtlasTaskDeleteSchemaShape = {
mode: z
.enum(["single", "bulk"])
.describe(
"Operation mode - 'single' for one task, 'bulk' for multiple tasks",
),
id: z
.string()
.optional()
.describe("Task ID to delete (required for mode='single')"),
taskIds: z
.array(z.string())
.optional()
.describe("Array of task IDs to delete (required for mode='bulk')"),
responseFormat: createResponseFormatEnum()
.optional()
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
} as const;
// Schema for validation
export const AtlasTaskDeleteSchema = z.discriminatedUnion("mode", [
SingleTaskSchema,
BulkTaskSchema,
]);
export type AtlasTaskDeleteInput = z.infer<typeof AtlasTaskDeleteSchema>;
export type AtlasTaskDeleteResponse = McpToolResponse;
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_database_clean/responseFormat.ts:
--------------------------------------------------------------------------------
```typescript
import { FormattedDatabaseCleanResponse } from "./types.js";
import { createToolResponse } from "../../../types/mcp.js"; // Import the new response creator
/**
* Defines a generic interface for formatting data into a string.
* This was previously imported but is now defined locally as the original seems to be removed.
*/
interface ResponseFormatter<T> {
format(data: T): string;
}
/**
* Formatter for database clean operation responses
*/
export class DatabaseCleanFormatter
implements ResponseFormatter<FormattedDatabaseCleanResponse>
{
format(data: FormattedDatabaseCleanResponse): string {
// Destructure without 'details' as it's no longer part of the interface or provided by the implementation
const { success, message, timestamp } = data;
// Create a summary section with operation results
const summaryHeader = success
? "Database Reset Successfully"
: "Database Reset Failed";
const summary =
`${summaryHeader}\n\n` +
`Status: ${success ? "✅ Success" : "❌ Failed"}\n` +
`Message: ${message}\n` +
`Timestamp: ${new Date(timestamp).toLocaleString()}\n`;
// Removed the 'detailsSection' as the implementation doesn't provide these details
// Add warning about permanent data loss
const warning =
"\n⚠️ WARNING\n" +
"This operation has permanently removed all data from the database. " +
"This action cannot be undone. If you need to restore the data, you must use a backup.";
// Return summary and warning only
return `${summary}${warning}`;
}
}
/**
* Create a formatted, human-readable response for the atlas_database_clean tool
*
* @param data The raw database clean response data
* @param isError Whether this response represents an error condition
* @returns Formatted MCP tool response with appropriate structure
*/
export function formatDatabaseCleanResponse(
data: FormattedDatabaseCleanResponse,
isError = false,
): any {
const formatter = new DatabaseCleanFormatter();
const formattedText = formatter.format(data);
return createToolResponse(formattedText, isError);
}
```
--------------------------------------------------------------------------------
/src/services/neo4j/backupRestoreService/scripts/db-backup.ts:
--------------------------------------------------------------------------------
```typescript
#!/usr/bin/env node
import { exportDatabase } from "../../index.js"; // Adjusted path
import { closeNeo4jConnection } from "../../index.js"; // Adjusted path
import { logger, requestContextService } from "../../../../utils/index.js"; // Adjusted path
import { config } from "../../../../config/index.js"; // Adjusted path
import { McpLogLevel } from "../../../../utils/internal/logger.js"; // Added McpLogLevel import
/**
* Manual backup script entry point.
* This script is intended to be run from the project root.
*/
const runManualBackup = async () => {
// Initialize logger for standalone script execution
await logger.initialize(config.logLevel as McpLogLevel);
logger.info("Starting manual database backup...");
try {
const backupPath = await exportDatabase();
logger.info(
`Manual backup completed successfully. Backup created at: ${backupPath}`,
);
} catch (error) {
const reqContext = requestContextService.createRequestContext({
operation: "runManualBackup.catch",
});
// Ensure logger is initialized before trying to use it in catch, though it should be by now.
if (!logger["initialized"]) {
// Accessing private member for a check, not ideal but pragmatic for script
console.error(
"Logger not initialized during catch block. Original error:",
error,
);
} else {
logger.error(
"Manual database backup failed:",
error as Error,
reqContext,
);
}
process.exitCode = 1; // Indicate failure
} finally {
// Ensure the Neo4j connection is closed after the script runs
// Also ensure logger is available for these final messages
if (!logger["initialized"]) {
console.info("Closing Neo4j connection (logger was not initialized)...");
} else {
logger.info("Closing Neo4j connection...");
}
await closeNeo4jConnection();
if (!logger["initialized"]) {
console.info("Neo4j connection closed (logger was not initialized).");
} else {
logger.info("Neo4j connection closed.");
}
}
};
// Execute the backup process
runManualBackup();
```
--------------------------------------------------------------------------------
/src/services/neo4j/index.ts:
--------------------------------------------------------------------------------
```typescript
/**
* Neo4j Services Module
*
* This module exports all Neo4j database services to provide a unified API for interacting
* with the Neo4j graph database. It encapsulates the complexity of Neo4j queries and
* transactions, providing a clean interface for application code.
*/
// Export core database driver and utilities
// Removed: export { autoExportManager } from './backup_services/autoExportManager.js';
export { neo4jDriver } from "./driver.js";
export { databaseEvents, DatabaseEventType } from "./events.js";
export * from "./helpers.js";
export { Neo4jUtils } from "./utils.js";
// Export entity services
// Removed backup_services exports
export { KnowledgeService } from "./knowledgeService.js";
export { ProjectService } from "./projectService.js";
export { SearchService } from "./searchService/index.js";
export type { SearchResultItem } from "./searchService/index.js";
export { TaskService } from "./taskService.js";
export {
exportDatabase,
importDatabase,
} from "./backupRestoreService/index.js";
export type { FullExport } from "./backupRestoreService/index.js";
// Export common types
export * from "./types.js";
/**
* Initialize the Neo4j database and related services
* Should be called at application startup
*/
// Removed initializeNeo4jServices function as it relied on backup_services
/**
* Initialize the Neo4j database schema
* Should be called at application startup
*/
export async function initializeNeo4jSchema(): Promise<void> {
const { Neo4jUtils } = await import("./utils.js");
return Neo4jUtils.initializeSchema();
}
// Removed restoreFromLatestBackup function
// Removed getLatestBackupFile function
// Removed createManualBackup function
/**
* Clear and reset the Neo4j database
* WARNING: This permanently deletes all data
*/
export async function clearNeo4jDatabase(): Promise<void> {
const { Neo4jUtils } = await import("./utils.js");
return Neo4jUtils.clearDatabase();
}
/**
* Close the Neo4j database connection
* Should be called when shutting down the application
*/
export async function closeNeo4jConnection(): Promise<void> {
const { neo4jDriver } = await import("./driver.js");
return neo4jDriver.close();
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_knowledge_delete/types.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from "zod";
import {
McpToolResponse,
ResponseFormat,
createResponseFormatEnum,
} from "../../../types/mcp.js";
// Schema for individual knowledge item removal
const SingleKnowledgeSchema = z
.object({
mode: z.literal("single"),
id: z
.string()
.describe("Knowledge item identifier to remove from the system"),
responseFormat: createResponseFormatEnum()
.optional()
.default(ResponseFormat.FORMATTED)
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
})
.describe("Remove a specific knowledge item by its unique identifier");
// Schema for multi-knowledge cleanup operation
const BulkKnowledgeSchema = z
.object({
mode: z.literal("bulk"),
knowledgeIds: z
.array(z.string())
.min(1)
.describe(
"Collection of knowledge identifiers to remove in a single operation",
),
responseFormat: createResponseFormatEnum()
.optional()
.default(ResponseFormat.FORMATTED)
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
})
.describe(
"Batch removal of multiple knowledge items in a single transaction",
);
// Schema shapes for tool registration
export const AtlasKnowledgeDeleteSchemaShape = {
mode: z
.enum(["single", "bulk"])
.describe(
"Operation mode - 'single' for individual removal, 'bulk' for batch operations",
),
id: z
.string()
.optional()
.describe("Knowledge ID to delete (required for mode='single')"),
knowledgeIds: z
.array(z.string())
.optional()
.describe("Array of knowledge IDs to delete (required for mode='bulk')"),
responseFormat: createResponseFormatEnum()
.optional()
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
} as const;
// Schema for validation
export const AtlasKnowledgeDeleteSchema = z.discriminatedUnion("mode", [
SingleKnowledgeSchema,
BulkKnowledgeSchema,
]);
export type AtlasKnowledgeDeleteInput = z.infer<
typeof AtlasKnowledgeDeleteSchema
>;
export type AtlasKnowledgeDeleteResponse = McpToolResponse;
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_database_clean/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import {
BaseErrorCode,
DatabaseExportImportErrorCode,
} from "../../../types/errors.js";
import {
createToolExample,
createToolMetadata,
registerTool,
} from "../../../types/tool.js";
import { atlasDatabaseClean } from "./cleanDatabase.js";
import { AtlasDatabaseCleanSchemaShape } from "./types.js";
export const registerAtlasDatabaseCleanTool = (server: McpServer) => {
registerTool(
server,
"atlas_database_clean",
"Completely resets the database - permanently removes all data from all entity types (projects, tasks, and knowledge)",
AtlasDatabaseCleanSchemaShape,
atlasDatabaseClean,
createToolMetadata({
examples: [
createToolExample(
{ acknowledgement: true },
`{
"success": true,
"message": "Database has been completely reset and schema reinitialized",
"timestamp": "2025-03-23T13:07:55.621Z",
"details": {
"schemaInitialized": true
}
}`,
"Reset the entire database and reinitialize the schema",
),
],
requiredPermission: "database:admin",
returnSchema: z.object({
success: z.boolean().describe("Operation success status"),
message: z.string().describe("Result message"),
timestamp: z.string().describe("Operation timestamp"),
details: z
.object({
schemaInitialized: z
.boolean()
.optional()
.describe("Schema reinitialization status"),
deletedRelationships: z
.number()
.optional()
.describe("Number of deleted relationships"),
deletedNodes: z
.number()
.optional()
.describe("Number of deleted nodes"),
})
.optional()
.describe("Detailed operation statistics"),
}),
rateLimit: {
windowMs: 60 * 60 * 1000, // 1 hour
maxRequests: 1, // 1 request per hour (since this is a destructive operation)
},
// Warning: This operation permanently deletes ALL data from the Atlas database
}),
);
};
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_task_list/types.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from "zod";
import {
McpToolResponse,
PriorityLevel,
TaskStatus,
} from "../../../types/mcp.js";
import { Neo4jTask } from "../../../services/neo4j/types.js";
// Schema for the tool input
export const TaskListRequestSchema = z.object({
projectId: z
.string()
.describe("ID of the project to list tasks for (required)"),
status: z
.union([
z.enum([
TaskStatus.BACKLOG,
TaskStatus.TODO,
TaskStatus.IN_PROGRESS,
TaskStatus.COMPLETED,
]),
z.array(
z.enum([
TaskStatus.BACKLOG,
TaskStatus.TODO,
TaskStatus.IN_PROGRESS,
TaskStatus.COMPLETED,
]),
),
])
.optional()
.describe("Filter by task status or array of statuses"),
assignedTo: z.string().optional().describe("Filter by assignment ID"),
priority: z
.union([
z.enum([
PriorityLevel.LOW,
PriorityLevel.MEDIUM,
PriorityLevel.HIGH,
PriorityLevel.CRITICAL,
]),
z.array(
z.enum([
PriorityLevel.LOW,
PriorityLevel.MEDIUM,
PriorityLevel.HIGH,
PriorityLevel.CRITICAL,
]),
),
])
.optional()
.describe("Filter by priority level or array of priorities"),
tags: z
.array(z.string())
.optional()
.describe(
"Array of tags to filter by (tasks matching any tag will be included)",
),
taskType: z.string().optional().describe("Filter by task classification"),
sortBy: z
.enum(["priority", "createdAt", "status"])
.optional()
.default("createdAt")
.describe("Field to sort results by (Default: createdAt)"),
sortDirection: z
.enum(["asc", "desc"])
.optional()
.default("desc")
.describe("Sort order (Default: desc)"),
page: z
.number()
.int()
.positive()
.optional()
.default(1)
.describe("Page number for paginated results (Default: 1)"),
limit: z
.number()
.int()
.positive()
.max(100)
.optional()
.default(20)
.describe("Number of results per page, maximum 100 (Default: 20)"),
});
export type TaskListRequestInput = z.infer<typeof TaskListRequestSchema>;
export interface TaskListResponse {
tasks: Neo4jTask[];
total: number;
page: number;
limit: number;
totalPages: number;
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_project_delete/types.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from "zod";
import {
McpToolResponse,
ResponseFormat,
createResponseFormatEnum,
} from "../../../types/mcp.js";
// Schema for individual project removal
const SingleProjectSchema = z
.object({
mode: z.literal("single"),
id: z
.string()
.describe("Project identifier to permanently remove from the system"),
responseFormat: createResponseFormatEnum()
.optional()
.default(ResponseFormat.FORMATTED)
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
})
.describe("Remove a specific project entity by its unique identifier");
// Schema for multi-project cleanup operation
const BulkProjectSchema = z
.object({
mode: z.literal("bulk"),
projectIds: z
.array(z.string())
.min(1)
.describe(
"Collection of project identifiers to remove in a single operation",
),
responseFormat: createResponseFormatEnum()
.optional()
.default(ResponseFormat.FORMATTED)
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
})
.describe(
"Batch removal of multiple project entities in a single transaction",
);
// Schema shapes for tool registration
export const AtlasProjectDeleteSchemaShape = {
mode: z
.enum(["single", "bulk"])
.describe(
"Operation strategy - 'single' for individual removal with detailed feedback, 'bulk' for efficient batch operations with aggregated results",
),
id: z
.string()
.optional()
.describe(
"Target project identifier for permanent removal including all associated tasks and knowledge (required for mode='single')",
),
projectIds: z
.array(z.string())
.optional()
.describe(
"Collection of project identifiers to permanently remove in a single atomic transaction (required for mode='bulk')",
),
responseFormat: createResponseFormatEnum()
.optional()
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
} as const;
// Schema for validation
export const AtlasProjectDeleteSchema = z.discriminatedUnion("mode", [
SingleProjectSchema,
BulkProjectSchema,
]);
export type AtlasProjectDeleteInput = z.infer<typeof AtlasProjectDeleteSchema>;
export type AtlasProjectDeleteResponse = McpToolResponse;
```
--------------------------------------------------------------------------------
/src/webui/styling/base.css:
--------------------------------------------------------------------------------
```css
/* ==========================================================================
Base Styles
========================================================================== */
body {
font-family: var(--font-family-sans-serif);
margin: 0;
background-color: var(--bg-color);
color: var(--text-color);
line-height: 1.65;
font-size: 16px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
transition:
background-color 0.2s ease-out,
color 0.2s ease-out;
}
/* Visually hidden class for accessibility */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* ==========================================================================
Typography
========================================================================== */
h1,
h2,
h3 {
color: var(--text-color);
margin-top: 0;
font-weight: 600; /* Semi-bold for headings */
}
h1 {
font-size: 2.25rem; /* 36px */
margin-bottom: var(--spacing-lg);
text-align: center;
color: var(--primary-color);
font-weight: 700; /* Bold for main title */
}
h2 {
/* Section titles like "Project Details" */
font-size: 1.75rem; /* 28px */
margin-bottom: var(--spacing-md);
padding-bottom: var(--spacing-sm);
border-bottom: 1px solid var(--border-color); /* Softer underline */
}
h3 {
/* Sub-section titles like "Tasks", "Knowledge Items" */
font-size: 1.375rem; /* 22px */
margin-bottom: var(--spacing-md);
color: var(--text-color);
}
label {
display: block;
margin-bottom: var(--spacing-sm);
font-weight: 500; /* Medium weight */
color: var(--secondary-text-color);
font-size: 0.9rem;
}
p {
margin-bottom: var(--spacing-md);
}
a {
color: var(--primary-color);
text-decoration: none;
transition: color 0.2s ease-out;
}
a:hover,
a:focus {
color: var(--primary-hover-color);
text-decoration: underline;
outline: none; /* Handled by box-shadow on focus for interactive elements */
}
pre {
/* General preformatted text styling */
background-color: var(--code-bg-color);
color: var(--text-color);
padding: var(--spacing-sm);
border-radius: var(--border-radius-sm);
white-space: pre-wrap;
word-wrap: break-word;
font-family: var(--font-family-monospace);
font-size: 0.9rem;
border: 1px solid var(--code-border-color);
max-height: 250px;
overflow-y: auto;
margin-top: var(--spacing-xs);
transition:
background-color 0.2s ease-out,
border-color 0.2s ease-out,
color 0.2s ease-out;
}
```
--------------------------------------------------------------------------------
/src/webui/logic/dom-elements.js:
--------------------------------------------------------------------------------
```javascript
/**
* @fileoverview Centralized DOM element selections.
* @module src/webui/logic/dom-elements
*/
/**
* Object containing references to frequently used DOM elements.
* @type {object}
* @property {HTMLElement} app - The main application container.
* @property {HTMLSelectElement} projectSelect - The project selection dropdown.
* @property {HTMLButtonElement} refreshButton - The button to refresh project list.
* @property {HTMLElement} projectDetailsContainer - Container for project details.
* @property {HTMLElement} detailsContent - Content area for project details.
* @property {HTMLElement} tasksContainer - Container for tasks.
* @property {HTMLElement} tasksContent - Content area for tasks.
* @property {HTMLElement} knowledgeContainer - Container for knowledge items.
* @property {HTMLElement} knowledgeContent - Content area for knowledge items.
* @property {HTMLElement} errorMessageDiv - Div to display error messages.
* @property {HTMLElement} neo4jStatusSpan - Span to display Neo4j connection status.
* @property {HTMLInputElement} themeCheckbox - Checkbox for theme toggling.
* @property {HTMLElement} themeLabel - Label for the theme toggle.
* @property {HTMLButtonElement} taskViewModeToggle - Button to toggle task view mode.
* @property {HTMLButtonElement} taskFlowToggle - Button to toggle task flow view.
* @property {HTMLElement} taskFlowContainer - Container for the task flow diagram.
* @property {HTMLButtonElement} knowledgeViewModeToggle - Button to toggle knowledge view mode.
*/
export const dom = {
app: document.getElementById("app"),
projectSelect: document.getElementById("project-select"),
refreshButton: document.getElementById("refresh-button"),
projectDetailsContainer: document.getElementById("project-details-container"),
detailsContent: document.getElementById("details-content"),
tasksContainer: document.getElementById("tasks-container"),
tasksContent: document.getElementById("tasks-content"),
knowledgeContainer: document.getElementById("knowledge-container"),
knowledgeContent: document.getElementById("knowledge-content"),
errorMessageDiv: document.getElementById("error-message"),
neo4jStatusSpan: document.getElementById("neo4j-status"),
themeCheckbox: document.getElementById("theme-checkbox"),
themeLabel: document.querySelector(".theme-label"),
taskViewModeToggle: document.getElementById("task-view-mode-toggle"),
taskFlowToggle: document.getElementById("task-flow-toggle"),
taskFlowContainer: document.getElementById("task-flow-container"),
knowledgeViewModeToggle: document.getElementById(
"knowledge-view-mode-toggle",
),
};
```
--------------------------------------------------------------------------------
/src/webui/styling/theme.css:
--------------------------------------------------------------------------------
```css
/* ==========================================================================
Global Variables & Theme Setup
========================================================================== */
:root {
/* Light Mode (Default) - Modern Minimalist */
--bg-color: #f4f7f9; /* Lighter, softer background */
--text-color: #333; /* Dark grey for text, not pure black */
--primary-color: #007bff; /* Standard blue accent */
--primary-hover-color: #0056b3;
--secondary-text-color: #555; /* Slightly darker secondary text */
--border-color: #e0e0e0; /* Softer borders */
--card-bg-color: #ffffff;
--shadow-color: rgba(0, 0, 0, 0.05); /* Softer shadow */
--error-color: #d9534f;
--error-bg-color: #fdf7f7;
--error-border-color: #f0c9c8;
--success-color: #5cb85c;
--warning-color: #f0ad4e;
--warning-bg-color: #fcf8e3; /* For backgrounds if needed */
--code-bg-color: #f9f9f9; /* Very light grey for code blocks */
--code-border-color: #efefef;
--connection-status-bg: #efefef;
--data-section-bg: var(
--card-bg-color
); /* Sections same as card for minimalism */
--data-item-border-color: #eaeaea; /* Softer item border */
--button-text-color: #ffffff;
--button-secondary-bg: #6c757d;
--button-secondary-hover-bg: #5a6268;
--font-family-sans-serif:
"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, sans-serif;
--font-family-monospace:
SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
--border-radius-sm: 4px;
--border-radius-md: 8px;
--border-radius-lg: 12px;
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
}
.dark-mode {
--bg-color: #121212; /* Very dark background */
--text-color: #e0e0e0;
/* --primary-color: #007bff; /* Can adjust for dark mode if needed, e.g., a lighter blue */
/* --primary-hover-color: #0056b3; */
--secondary-text-color: #aaa;
--border-color: #333; /* Darker, subtle borders */
--card-bg-color: #1e1e1e; /* Dark card background */
--shadow-color: rgba(255, 255, 255, 0.03); /* Very subtle light shadow */
--error-color: #e77773;
--error-bg-color: #3d2727;
--error-border-color: #5c4040;
--success-color: #7bc77b;
--warning-color: #f5c76e;
--warning-bg-color: #4a412a; /* For backgrounds if needed */
--code-bg-color: #282828;
--code-border-color: #383838;
--connection-status-bg: #282828;
--data-section-bg: var(--card-bg-color);
--data-item-border-color: #333;
/* --button-text-color: #ffffff; */ /* Usually remains white */
--button-secondary-bg: #495057;
--button-secondary-hover-bg: #343a40;
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_knowledge_list/types.ts:
--------------------------------------------------------------------------------
```typescript
import { KnowledgeFilterOptions } from "../../../services/neo4j/types.js";
import { Neo4jKnowledge } from "../../../services/neo4j/types.js";
/**
* Query parameters for retrieving and filtering knowledge items
*/
export interface KnowledgeListRequest {
/** ID of the project to list knowledge items for (required) */
projectId: string;
/** Array of tags to filter by (items matching any tag will be included) */
tags?: string[];
/** Filter by knowledge domain/category */
domain?: string;
/** Text search query to filter results by content relevance */
search?: string;
/** Page number for paginated results (Default: 1) */
page?: number;
/** Number of results per page, maximum 100 (Default: 20) */
limit?: number;
}
/**
* Response structure for knowledge queries
*/
export interface KnowledgeListResponse {
/** Collection of knowledge items matching search criteria */
knowledge: KnowledgeItem[];
/** Total record count matching criteria (pre-pagination) */
total: number;
/** Current pagination position */
page: number;
/** Pagination size setting */
limit: number;
/** Total available pages for the current query */
totalPages: number;
}
/**
* Knowledge item entity structure for API responses
*/
export interface KnowledgeItem {
/** Neo4j internal node identifier */
identity?: number;
/** Neo4j node type designations */
labels?: string[];
/** Core knowledge item attributes */
properties?: {
/** Unique knowledge item identifier */
id: string;
/** Project this knowledge belongs to */
projectId: string;
/** Knowledge content */
text: string;
/** Categorical labels for organization and filtering */
tags?: string[];
/** Primary knowledge area/discipline */
domain: string;
/** Reference sources supporting this knowledge */
citations?: string[];
/** Creation timestamp (ISO format) */
createdAt: string;
/** Last modification timestamp (ISO format) */
updatedAt: string;
};
/** Neo4j element identifier */
elementId?: string;
/** Unique knowledge item identifier */
id: string;
/** Project this knowledge belongs to */
projectId: string;
/** Knowledge content */
text: string;
/** Categorical labels for organization and filtering */
tags?: string[];
/** Primary knowledge area/discipline */
domain: string;
/** Reference sources supporting this knowledge */
citations?: string[];
/** Creation timestamp (ISO format) */
createdAt: string;
/** Last modification timestamp (ISO format) */
updatedAt: string;
/** Associated project name (for context) */
projectName?: string;
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_unified_search/types.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from "zod";
import { SearchResultItem } from "../../../services/neo4j/index.js";
// Schema for the tool input
export const UnifiedSearchRequestSchema = z.object({
property: z
.string()
.optional()
.describe(
"Optional: Target a specific property for search. If specified, a regex-based search is performed on this property (e.g., 'name', 'description', 'text', 'tags', 'urls'). If omitted, a full-text index search is performed across default fields for each entity type (typically includes fields like name, title, description, text, tags, but depends on index configuration).",
),
value: z.string().describe("The search term or phrase."),
entityTypes: z
.array(z.enum(["project", "task", "knowledge"]))
.optional()
.describe(
"Array of entity types ('project', 'task', 'knowledge') to include in search (Default: all types if omitted)",
),
caseInsensitive: z
.boolean()
.optional()
.default(true)
.describe(
"For regex search (when 'property' is specified): Perform a case-insensitive search (Default: true). Not applicable to full-text index searches (when 'property' is omitted).",
),
fuzzy: z
.boolean()
.optional()
.default(true) // Changed default to true for more intuitive "contains" search on specific properties
.describe(
"For regex search (when 'property' is specified): Enables 'contains' matching (Default: true). Set to false for an exact match on the property. For full-text search (when 'property' is omitted): If true, attempts to construct a fuzzy Lucene query (e.g., term~1); if false (default for this case, as Zod default is true but full-text might interpret it differently if not explicitly handled), performs a standard term match.",
),
taskType: z
.string()
.optional()
.describe(
"Optional filter by project/task classification (applies to project and task types)",
),
assignedToUserId: z
.string()
.optional()
.describe(
"Optional: Filter tasks by the ID of the assigned user. Only applicable when 'property' is specified (regex search) and 'entityTypes' includes 'task'.",
),
page: z
.number()
.int()
.positive()
.optional()
.default(1)
.describe("Page number for paginated results (Default: 1)"),
limit: z
.number()
.int()
.positive()
.max(100)
.optional()
.default(20)
.describe("Number of results per page, maximum 100 (Default: 20)"),
});
export type UnifiedSearchRequestInput = z.infer<
typeof UnifiedSearchRequestSchema
>;
export interface UnifiedSearchResponse {
results: SearchResultItem[];
total: number;
page: number;
limit: number;
totalPages: number;
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_database_clean/cleanDatabase.ts:
--------------------------------------------------------------------------------
```typescript
import { BaseErrorCode, McpError } from "../../../types/errors.js";
import { ResponseFormat, createToolResponse } from "../../../types/mcp.js";
import { logger, requestContextService } from "../../../utils/index.js"; // Import requestContextService
import { ToolContext } from "../../../types/tool.js";
import { formatDatabaseCleanResponse } from "./responseFormat.js";
import { AtlasDatabaseCleanInput, AtlasDatabaseCleanSchema } from "./types.js";
import { neo4jDriver } from "../../../services/neo4j/driver.js";
/**
* Execute a complete database reset operation
* This permanently removes all data from the Neo4j database
*
* @param input No input parameters required
* @param context Tool execution context
* @returns Formatted response with operation results
*/
export const atlasDatabaseClean = async (
input: unknown,
context: ToolContext,
) => {
const reqContext =
context.requestContext ??
requestContextService.createRequestContext({
toolName: "atlasDatabaseClean",
});
try {
// Parse and validate input against schema (should be empty object)
const validatedInput: AtlasDatabaseCleanInput =
AtlasDatabaseCleanSchema.parse(input);
// Log the operation start
logger.warning("Executing complete database reset operation", reqContext);
// Track execution metrics
const startTime = Date.now();
try {
// Execute a very simple query that will delete all relationships and nodes
await neo4jDriver.executeQuery("MATCH (n) DETACH DELETE n");
// Calculate execution duration
const executionTime = Date.now() - startTime;
// Log successful operation
logger.warning("Database reset completed successfully", {
...reqContext,
executionTime: `${executionTime}ms`,
});
const result = {
success: true,
message:
"Database has been completely reset - all nodes and relationships removed",
timestamp: new Date().toISOString(),
};
// Conditionally format response
if (validatedInput.responseFormat === ResponseFormat.JSON) {
return createToolResponse(JSON.stringify(result, null, 2));
} else {
return formatDatabaseCleanResponse(result);
}
} catch (error) {
throw new Error(
`Failed to execute database reset: ${error instanceof Error ? error.message : String(error)}`,
);
}
} catch (error) {
// Log error
logger.error("Failed to reset database", error as Error, {
...reqContext,
inputReceived: input,
});
// Handle specific error cases
if (error instanceof McpError) {
throw error;
}
// Convert generic errors to McpError
throw new McpError(
BaseErrorCode.INTERNAL_ERROR,
`Error resetting database: ${error instanceof Error ? error.message : "Unknown error"}`,
);
}
};
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_task_delete/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import {
createToolExample,
createToolMetadata,
registerTool,
} from "../../../types/tool.js";
import { atlasDeleteTask } from "./deleteTask.js";
import { AtlasTaskDeleteSchemaShape } from "./types.js";
export const registerAtlasTaskDeleteTool = (server: McpServer) => {
registerTool(
server,
"atlas_task_delete",
"Deletes existing task(s) from the system with support for both single task removal and bulk deletion operations",
AtlasTaskDeleteSchemaShape,
atlasDeleteTask,
createToolMetadata({
examples: [
createToolExample(
{
mode: "single",
id: "task_api_gateway",
},
`{
"success": true,
"message": "Task with ID task_api_gateway removed successfully",
"id": "task_api_gateway"
}`,
"Remove a completed task from the system",
),
createToolExample(
{
mode: "bulk",
taskIds: ["task_graphql_schema", "task_auth", "task_old_feature"],
},
`{
"success": true,
"message": "Successfully removed 3 tasks",
"deleted": ["task_graphql_schema", "task_auth", "task_old_feature"],
"errors": []
}`,
"Clean up multiple completed or deprecated tasks in a single operation",
),
],
requiredPermission: "task:delete",
returnSchema: z.union([
// Single task deletion response
z.object({
id: z.string().describe("Task ID"),
success: z.boolean().describe("Operation success status"),
message: z.string().describe("Result message"),
}),
// Bulk deletion response
z.object({
success: z.boolean().describe("Operation success status"),
message: z.string().describe("Result message"),
deleted: z
.array(z.string())
.describe("IDs of successfully deleted tasks"),
errors: z
.array(
z.object({
taskId: z.string().describe("Task ID that failed to delete"),
error: z
.object({
code: z.string().describe("Error code"),
message: z.string().describe("Error message"),
details: z
.any()
.optional()
.describe("Additional error details"),
})
.describe("Error information"),
}),
)
.describe("Deletion errors"),
}),
]),
rateLimit: {
windowMs: 60 * 1000, // 1 minute
maxRequests: 15, // 15 requests per minute (either single or bulk)
},
}),
);
};
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_task_list/listTasks.ts:
--------------------------------------------------------------------------------
```typescript
import { TaskService } from "../../../services/neo4j/taskService.js";
import { ProjectService } from "../../../services/neo4j/projectService.js";
import {
BaseErrorCode,
McpError,
ProjectErrorCode,
} from "../../../types/errors.js";
import { logger, requestContextService } from "../../../utils/index.js"; // Import requestContextService
import { ToolContext } from "../../../types/tool.js";
import { TaskListRequestInput, TaskListRequestSchema } from "./types.js";
import { formatTaskListResponse } from "./responseFormat.js";
export const atlasListTasks = async (input: unknown, context: ToolContext) => {
const reqContext =
context.requestContext ??
requestContextService.createRequestContext({ toolName: "atlasListTasks" });
try {
// Parse and validate input against schema
const validatedInput = TaskListRequestSchema.parse(input);
// Log operation
logger.info("Listing tasks for project", {
...reqContext,
projectId: validatedInput.projectId,
});
// First check if the project exists
const projectExists = await ProjectService.getProjectById(
validatedInput.projectId,
);
if (!projectExists) {
throw new McpError(
ProjectErrorCode.PROJECT_NOT_FOUND,
`Project with ID ${validatedInput.projectId} not found`,
{ projectId: validatedInput.projectId },
);
}
// Call the task service to get tasks with filters
const tasksResult = await TaskService.getTasks({
projectId: validatedInput.projectId,
status: validatedInput.status,
assignedTo: validatedInput.assignedTo,
priority: validatedInput.priority,
tags: validatedInput.tags,
taskType: validatedInput.taskType,
sortBy: validatedInput.sortBy,
sortDirection: validatedInput.sortDirection,
page: validatedInput.page,
limit: validatedInput.limit,
});
logger.info("Tasks retrieved successfully", {
...reqContext,
projectId: validatedInput.projectId,
taskCount: tasksResult.data.length,
totalTasks: tasksResult.total,
});
// Create the response object with task data
const responseData = {
tasks: tasksResult.data,
total: tasksResult.total,
page: tasksResult.page,
limit: tasksResult.limit,
totalPages: tasksResult.totalPages,
};
// Return the raw response data object
return responseData;
} catch (error) {
// Handle specific error cases
if (error instanceof McpError) {
throw error;
}
logger.error("Failed to list tasks", error as Error, {
...reqContext,
inputReceived: input, // validatedInput might not be defined here
});
// Convert other errors to McpError
throw new McpError(
BaseErrorCode.INTERNAL_ERROR,
`Error listing tasks: ${error instanceof Error ? error.message : "Unknown error"}`,
);
}
};
```
--------------------------------------------------------------------------------
/src/services/neo4j/events.ts:
--------------------------------------------------------------------------------
```typescript
import { EventEmitter } from "events";
import { logger, requestContextService } from "../../utils/index.js"; // Updated import path
/**
* Event types for database operations
*/
export enum DatabaseEventType {
WRITE_OPERATION = "write_operation",
READ_OPERATION = "read_operation",
TRANSACTION_COMPLETE = "transaction_complete",
ERROR = "error",
}
/**
* Database event system to facilitate communication between services
* Uses the publish-subscribe pattern to decouple components
*/
class DatabaseEventSystem {
private emitter: EventEmitter;
private static instance: DatabaseEventSystem;
private constructor() {
this.emitter = new EventEmitter();
// Set a higher limit for listeners to avoid warnings
this.emitter.setMaxListeners(20);
// Log all events in debug mode
if (process.env.NODE_ENV === "development") {
this.emitter.on(DatabaseEventType.WRITE_OPERATION, (details) => {
const reqContext = requestContextService.createRequestContext({
operation: "DatabaseEvent.WRITE_OPERATION",
eventDetails: details,
});
logger.debug("Database write operation", reqContext);
});
this.emitter.on(DatabaseEventType.ERROR, (error) => {
const reqContext = requestContextService.createRequestContext({
operation: "DatabaseEvent.ERROR",
eventError: error,
});
logger.debug("Database event error", reqContext);
});
}
}
/**
* Get the singleton instance
*/
public static getInstance(): DatabaseEventSystem {
if (!DatabaseEventSystem.instance) {
DatabaseEventSystem.instance = new DatabaseEventSystem();
}
return DatabaseEventSystem.instance;
}
/**
* Subscribe to a database event
* @param eventType Event type to subscribe to
* @param listener Function to call when the event occurs
*/
public subscribe<T>(
eventType: DatabaseEventType,
listener: (data: T) => void,
): void {
this.emitter.on(eventType, listener);
}
/**
* Unsubscribe from a database event
* @param eventType Event type to unsubscribe from
* @param listener Function to remove
*/
public unsubscribe<T>(
eventType: DatabaseEventType,
listener: (data: T) => void,
): void {
this.emitter.off(eventType, listener);
}
/**
* Publish a database event
* @param eventType Event type to publish
* @param data Event data
*/
public publish<T>(eventType: DatabaseEventType, data: T): void {
this.emitter.emit(eventType, data);
}
/**
* Subscribe to a database event only once
* @param eventType Event type to subscribe to
* @param listener Function to call when the event occurs
*/
public subscribeOnce<T>(
eventType: DatabaseEventType,
listener: (data: T) => void,
): void {
this.emitter.once(eventType, listener);
}
}
// Export singleton instance
export const databaseEvents = DatabaseEventSystem.getInstance();
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_project_delete/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import {
createToolExample,
createToolMetadata,
registerTool,
} from "../../../types/tool.js";
import { atlasDeleteProject } from "./deleteProject.js";
import { AtlasProjectDeleteSchemaShape } from "./types.js";
export const registerAtlasProjectDeleteTool = (server: McpServer) => {
registerTool(
server,
"atlas_project_delete",
"Removes project entities and associated resources from the system with cascading deletion of linked tasks and knowledge items",
AtlasProjectDeleteSchemaShape,
atlasDeleteProject,
createToolMetadata({
examples: [
createToolExample(
{
mode: "single",
id: "proj_ms_migration",
},
`{
"success": true,
"message": "Project with ID proj_ms_migration deleted successfully",
"id": "proj_ms_migration"
}`,
"Remove a completed engineering project from the system",
),
createToolExample(
{
mode: "bulk",
projectIds: ["proj_graphql", "proj_perf", "proj_deprecated_api"],
},
`{
"success": true,
"message": "Successfully deleted 3 projects",
"deleted": ["proj_graphql", "proj_perf", "proj_deprecated_api"],
"errors": []
}`,
"Clean up multiple completed or deprecated projects in a single atomic operation",
),
],
requiredPermission: "project:delete",
returnSchema: z.union([
// Single project deletion response
z.object({
id: z.string().describe("Project ID"),
success: z.boolean().describe("Operation success status"),
message: z.string().describe("Result message"),
}),
// Bulk deletion response
z.object({
success: z.boolean().describe("Operation success status"),
message: z.string().describe("Result message"),
deleted: z
.array(z.string())
.describe("IDs of successfully deleted projects"),
errors: z
.array(
z.object({
projectId: z
.string()
.describe("Project ID that failed to delete"),
error: z
.object({
code: z.string().describe("Error code"),
message: z.string().describe("Error message"),
details: z
.any()
.optional()
.describe("Additional error details"),
})
.describe("Error information"),
}),
)
.describe("Deletion errors"),
}),
]),
rateLimit: {
windowMs: 60 * 1000, // 1 minute
maxRequests: 10, // 10 requests per minute (either single or bulk)
},
}),
);
};
```
--------------------------------------------------------------------------------
/src/webui/logic/app-state.js:
--------------------------------------------------------------------------------
```javascript
/**
* @fileoverview Manages global application state and provides utility functions.
* @module src/webui/logic/app-state
*/
/**
* Global application state.
* @type {object}
* @property {object|null} driver - Neo4j driver instance. Initialized by api-service.
* @property {string|null} currentProjectId - ID of the currently selected project.
* @property {object|null} currentProject - Details of the currently selected project.
* @property {Array<object>} currentTasks - List of tasks for the current project.
* @property {Array<object>} currentKnowledgeItems - List of knowledge items for the current project.
* @property {string} tasksViewMode - Current view mode for tasks ('detailed' or 'compact').
* @property {string} knowledgeViewMode - Current view mode for knowledge items ('detailed' or 'compact').
* @property {boolean} showingTaskFlow - Flag indicating if the task flow diagram is visible.
*/
export const state = {
driver: null,
currentProjectId: null,
currentProject: null,
currentTasks: [],
currentKnowledgeItems: [],
tasksViewMode: "detailed", // 'detailed' or 'compact'
knowledgeViewMode: "detailed", // 'detailed' or 'compact'
showingTaskFlow: false,
};
/**
* Utility functions for common tasks.
* @type {object}
*/
export const utils = {
/**
* Escapes HTML special characters in a string.
* @param {string|null|undefined} unsafe - The string to escape.
* @returns {string} The escaped string, or "N/A" if input is null/undefined.
*/
escapeHtml: (unsafe) => {
if (unsafe === null || typeof unsafe === "undefined") return "N/A";
return String(unsafe).replace(/[&<>"']/g, (match) => {
switch (match) {
case "&":
return "&";
case "<":
return "<";
case ">":
return ">";
case '"':
return """;
case "'":
return "'";
default:
return match;
}
});
},
/**
* Safely parses a JSON string.
* @param {string|Array<any>} jsonString - The JSON string or an already parsed array.
* @param {Array<any>} [defaultValue=[]] - The default value to return on parsing failure.
* @returns {Array<any>} The parsed array or the default value.
*/
parseJsonSafe: (jsonString, defaultValue = []) => {
if (typeof jsonString !== "string") {
return Array.isArray(jsonString) ? jsonString : defaultValue;
}
try {
const parsed = JSON.parse(jsonString);
return Array.isArray(parsed) ? parsed : defaultValue;
} catch (e) {
console.warn("Failed to parse JSON string:", jsonString, e);
return defaultValue;
}
},
/**
* Formats a date string into a locale-specific string.
* @param {string|null|undefined} dateString - The date string to format.
* @returns {string} The formatted date string, "N/A", or "Invalid Date".
*/
formatDate: (dateString) => {
if (!dateString) return "N/A";
try {
return new Date(dateString).toLocaleString();
} catch (e) {
return "Invalid Date";
}
},
};
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_knowledge_delete/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import {
createToolExample,
createToolMetadata,
registerTool,
} from "../../../types/tool.js";
import { atlasDeleteKnowledge } from "./deleteKnowledge.js";
import { AtlasKnowledgeDeleteSchemaShape } from "./types.js";
export const registerAtlasKnowledgeDeleteTool = (server: McpServer) => {
registerTool(
server,
"atlas_knowledge_delete",
"Deletes existing knowledge item(s) from the system with support for individual removal and batch operations",
AtlasKnowledgeDeleteSchemaShape,
atlasDeleteKnowledge,
createToolMetadata({
examples: [
createToolExample(
{
mode: "single",
id: "know_graphql_benefits",
},
`{
"success": true,
"message": "Knowledge item with ID know_graphql_benefits removed successfully",
"id": "know_graphql_benefits"
}`,
"Remove a specific knowledge item from the system",
),
createToolExample(
{
mode: "bulk",
knowledgeIds: [
"know_api_design",
"know_security_best_practices",
"know_deprecated_methods",
],
},
`{
"success": true,
"message": "Successfully removed 3 knowledge items",
"deleted": ["know_api_design", "know_security_best_practices", "know_deprecated_methods"],
"errors": []
}`,
"Clean up multiple knowledge items in a single operation",
),
],
requiredPermission: "knowledge:delete",
returnSchema: z.union([
// Single knowledge deletion response
z.object({
id: z.string().describe("Knowledge ID"),
success: z.boolean().describe("Operation success status"),
message: z.string().describe("Result message"),
}),
// Bulk deletion response
z.object({
success: z.boolean().describe("Operation success status"),
message: z.string().describe("Result message"),
deleted: z
.array(z.string())
.describe("IDs of successfully deleted knowledge items"),
errors: z
.array(
z.object({
knowledgeId: z
.string()
.describe("Knowledge ID that failed to delete"),
error: z
.object({
code: z.string().describe("Error code"),
message: z.string().describe("Error message"),
details: z
.any()
.optional()
.describe("Additional error details"),
})
.describe("Error information"),
}),
)
.describe("Deletion errors"),
}),
]),
rateLimit: {
windowMs: 60 * 1000, // 1 minute
maxRequests: 10, // 10 requests per minute (either single or bulk)
},
}),
);
};
```
--------------------------------------------------------------------------------
/src/types/errors.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from "zod";
import { McpContent, McpToolResponse } from "./mcp.js";
// Base error codes that all tools can use
export enum BaseErrorCode {
UNAUTHORIZED = "UNAUTHORIZED",
RATE_LIMITED = "RATE_LIMITED",
VALIDATION_ERROR = "VALIDATION_ERROR",
INTERNAL_ERROR = "INTERNAL_ERROR",
NOT_FOUND = "NOT_FOUND",
PERMISSION_DENIED = "PERMISSION_DENIED",
SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE",
CONFIGURATION_ERROR = "CONFIGURATION_ERROR",
INITIALIZATION_FAILED = "INITIALIZATION_FAILED",
FORBIDDEN = "FORBIDDEN",
}
// Project-specific error codes
export enum ProjectErrorCode {
DUPLICATE_NAME = "DUPLICATE_NAME",
INVALID_STATUS = "INVALID_STATUS",
PROJECT_NOT_FOUND = "PROJECT_NOT_FOUND",
DEPENDENCY_CYCLE = "DEPENDENCY_CYCLE",
INVALID_DEPENDENCY = "INVALID_DEPENDENCY",
}
// Task-specific error codes
export enum TaskErrorCode {
TASK_NOT_FOUND = "TASK_NOT_FOUND",
INVALID_STATUS = "INVALID_STATUS",
INVALID_PRIORITY = "INVALID_PRIORITY",
INVALID_DEPENDENCY = "INVALID_DEPENDENCY",
DEPENDENCY_CYCLE = "DEPENDENCY_CYCLE",
}
// Note-specific error codes
export enum NoteErrorCode {
INVALID_TAGS = "INVALID_TAGS",
NOTE_NOT_FOUND = "NOTE_NOT_FOUND",
}
// Link-specific error codes
export enum LinkErrorCode {
INVALID_URL = "INVALID_URL",
LINK_NOT_FOUND = "LINK_NOT_FOUND",
DUPLICATE_URL = "DUPLICATE_URL",
}
// Member-specific error codes
export enum MemberErrorCode {
INVALID_ROLE = "INVALID_ROLE",
MEMBER_NOT_FOUND = "MEMBER_NOT_FOUND",
DUPLICATE_MEMBER = "DUPLICATE_MEMBER",
}
// Skill-specific error codes
export enum SkillErrorCode {
SKILL_NOT_FOUND = "SKILL_NOT_FOUND",
DEPENDENCY_NOT_FOUND = "DEPENDENCY_NOT_FOUND",
MISSING_PARAMETER = "MISSING_PARAMETER",
CIRCULAR_DEPENDENCY = "CIRCULAR_DEPENDENCY",
SKILL_EXECUTION_ERROR = "SKILL_EXECUTION_ERROR",
}
// Database export/import error codes
export enum DatabaseExportImportErrorCode {
EXPORT_ERROR = "EXPORT_ERROR",
IMPORT_ERROR = "IMPORT_ERROR",
FILE_ACCESS_ERROR = "FILE_ACCESS_ERROR",
INVALID_EXPORT_FORMAT = "INVALID_EXPORT_FORMAT",
RESET_FAILED = "RESET_FAILED",
INVALID_CONFIRMATION_CODE = "INVALID_CONFIRMATION_CODE",
PERMISSION_DENIED = "PERMISSION_DENIED",
}
// Base MCP error class
export class McpError extends Error {
constructor(
public code:
| BaseErrorCode
| ProjectErrorCode
| TaskErrorCode
| NoteErrorCode
| LinkErrorCode
| MemberErrorCode
| SkillErrorCode
| DatabaseExportImportErrorCode,
message: string,
public details?: Record<string, unknown>,
) {
super(message);
this.name = "McpError";
}
toResponse(): McpToolResponse {
const content: McpContent = {
type: "text",
text: `Error [${this.code}]: ${this.message}${
this.details
? `\nDetails: ${JSON.stringify(this.details, null, 2)}`
: ""
}`,
};
return {
content: [content],
isError: true,
_type: "tool_response",
};
}
}
// Error schema for validation
export const ErrorSchema = z.object({
code: z.string(),
message: z.string(),
details: z.record(z.unknown()).optional(),
});
export type ErrorResponse = z.infer<typeof ErrorSchema>;
```
--------------------------------------------------------------------------------
/scripts/clean.ts:
--------------------------------------------------------------------------------
```typescript
#!/usr/bin/env node
/**
* @fileoverview Utility script to clean build artifacts and temporary directories.
* @module scripts/clean
* By default, it removes the 'dist' and 'logs' directories.
* Custom directories can be specified as command-line arguments.
* Works on all platforms using Node.js path normalization.
*
* @example
* // Add to package.json:
* // "scripts": {
* // "clean": "ts-node --esm scripts/clean.ts",
* // "rebuild": "npm run clean && npm run build"
* // }
*
* // Run with default directories:
* // npm run clean
*
* // Run with custom directories:
* // ts-node --esm scripts/clean.ts temp coverage
*/
import { rm, access } from "fs/promises";
import { join } from "path";
/**
* Represents the result of a clean operation for a single directory.
* @property dir - The name of the directory targeted for cleaning.
* @property status - Indicates if the cleaning was successful or skipped.
* @property reason - If skipped, the reason why.
*/
interface CleanResult {
dir: string;
status: "success" | "skipped";
reason?: string;
}
/**
* Asynchronously checks if a directory exists at the given path.
* @param dirPath - The absolute or relative path to the directory.
* @returns A promise that resolves to `true` if the directory exists, `false` otherwise.
*/
async function directoryExists(dirPath: string): Promise<boolean> {
try {
await access(dirPath);
return true;
} catch {
return false;
}
}
/**
* Main function to perform the cleaning operation.
* It reads command line arguments for target directories or uses defaults ('dist', 'logs').
* Reports the status of each cleaning attempt.
*/
const clean = async (): Promise<void> => {
try {
let dirsToClean: string[] = ["dist", "logs"];
const args = process.argv.slice(2);
if (args.length > 0) {
dirsToClean = args;
}
console.log(`Attempting to clean directories: ${dirsToClean.join(", ")}`);
const results = await Promise.allSettled(
dirsToClean.map(async (dir): Promise<CleanResult> => {
const dirPath = join(process.cwd(), dir);
try {
const exists = await directoryExists(dirPath);
if (!exists) {
return { dir, status: "skipped", reason: "does not exist" };
}
await rm(dirPath, { recursive: true, force: true });
return { dir, status: "success" };
} catch (error) {
// Rethrow to be caught by Promise.allSettled's rejection case
throw error;
}
}),
);
results.forEach((result) => {
if (result.status === "fulfilled") {
const { dir, status, reason } = result.value;
if (status === "success") {
console.log(`Successfully cleaned directory: ${dir}`);
} else {
console.log(`Skipped cleaning directory ${dir}: ${reason}.`);
}
} else {
// The error here is the actual error object from the rejected promise
console.error(
`Error cleaning a directory (details below):\n`,
result.reason,
);
}
});
} catch (error) {
console.error(
"An unexpected error occurred during the clean script execution:",
error instanceof Error ? error.message : error,
);
process.exit(1);
}
};
clean();
```
--------------------------------------------------------------------------------
/src/mcp/transports/stdioTransport.ts:
--------------------------------------------------------------------------------
```typescript
/**
* @fileoverview Handles the setup and connection for the Stdio MCP transport.
* Implements the MCP Specification 2025-03-26 for stdio transport.
* This transport communicates directly over standard input (stdin) and
* standard output (stdout), typically used when the MCP server is launched
* as a child process by a host application.
*
* Specification Reference:
* https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx#stdio
*
* --- Authentication Note ---
* As per the MCP Authorization Specification (2025-03-26, Section 1.2),
* STDIO transports SHOULD NOT implement HTTP-based authentication flows.
* Authorization is typically handled implicitly by the host application
* controlling the server process. This implementation follows that guideline.
*
* @see {@link https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/authorization.mdx | MCP Authorization Specification}
* @module src/mcp-server/transports/stdioTransport
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ErrorHandler, logger, RequestContext } from "../../utils/index.js";
/**
* Connects a given `McpServer` instance to the Stdio transport.
* This function initializes the SDK's `StdioServerTransport`, which manages
* communication over `process.stdin` and `process.stdout` according to the
* MCP stdio transport specification.
*
* MCP Spec Points Covered by SDK's `StdioServerTransport`:
* - Reads JSON-RPC messages (requests, notifications, responses, batches) from stdin.
* - Writes JSON-RPC messages to stdout.
* - Handles newline delimiters and ensures no embedded newlines in output messages.
* - Ensures only valid MCP messages are written to stdout.
*
* Logging via the `logger` utility MAY result in output to stderr, which is
* permitted by the spec for logging purposes.
*
* @param server - The `McpServer` instance.
* @param parentContext - The logging and tracing context from the calling function.
* @returns A promise that resolves when the Stdio transport is successfully connected.
* @throws {Error} If the connection fails during setup.
*/
export async function connectStdioTransport(
server: McpServer,
parentContext: RequestContext,
): Promise<void> {
const operationContext = {
...parentContext,
operation: "connectStdioTransport",
transportType: "Stdio",
};
logger.debug("Attempting to connect stdio transport...", operationContext);
try {
logger.debug("Creating StdioServerTransport instance...", operationContext);
const transport = new StdioServerTransport();
logger.debug(
"Connecting McpServer instance to StdioServerTransport...",
operationContext,
);
await server.connect(transport);
logger.info(
"MCP Server connected and listening via stdio transport.",
operationContext,
);
if (process.stdout.isTTY) {
console.log(
`\n🚀 MCP Server running in STDIO mode.\n (MCP Spec: 2025-03-26 Stdio Transport)\n`,
);
}
} catch (err) {
ErrorHandler.handleError(err, { ...operationContext, critical: true });
throw err; // Re-throw after handling to allow caller to react if necessary
}
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_knowledge_list/responseFormat.ts:
--------------------------------------------------------------------------------
```typescript
import { createToolResponse } from "../../../types/mcp.js"; // Import the new response creator
import { KnowledgeListResponse } from "./types.js";
/**
* Defines a generic interface for formatting data into a string.
*/
interface ResponseFormatter<T> {
format(data: T): string;
}
/**
* Formatter for structured knowledge query responses
*/
export class KnowledgeListFormatter
implements ResponseFormatter<KnowledgeListResponse>
{
format(data: KnowledgeListResponse): string {
const { knowledge, total, page, limit, totalPages } = data;
// Generate result summary section
const summary =
`Knowledge Repository\n\n` +
`Total Items: ${total}\n` +
`Page: ${page} of ${totalPages}\n` +
`Displaying: ${Math.min(limit, knowledge.length)} item(s) per page\n`;
if (knowledge.length === 0) {
return `${summary}\n\nNo knowledge items matched the specified criteria`;
}
// Format each knowledge item
const knowledgeSections = knowledge
.map((item, index) => {
const {
id,
projectId,
projectName,
domain,
tags,
text,
citations,
createdAt,
updatedAt,
} = item;
let knowledgeSection =
`${index + 1}. ${domain || "Uncategorized"} Knowledge\n\n` +
`ID: ${id}\n` +
`Project: ${projectName || projectId}\n` +
`Domain: ${domain}\n`;
// Add tags if available
if (tags && tags.length > 0) {
knowledgeSection += `Tags: ${tags.join(", ")}\n`;
}
// Format dates
const createdDate = createdAt
? new Date(createdAt).toLocaleString()
: "Unknown Date";
const updatedDate = updatedAt
? new Date(updatedAt).toLocaleString()
: "Unknown Date";
knowledgeSection +=
`Created: ${createdDate}\n` + `Updated: ${updatedDate}\n\n`;
// Add knowledge content
knowledgeSection += `Content:\n${text || "No content available"}\n`;
// Add citations if available
if (citations && citations.length > 0) {
knowledgeSection += `\nCitations:\n`;
citations.forEach((citation, citIndex) => {
knowledgeSection += `${citIndex + 1}. ${citation}\n`;
});
}
return knowledgeSection;
})
.join("\n\n----------\n\n");
// Append pagination metadata for multi-page results
let paginationInfo = "";
if (totalPages > 1) {
paginationInfo =
`\n\nPagination Controls\n\n` +
`Viewing page ${page} of ${totalPages}.\n` +
`${page < totalPages ? "Use 'page' parameter to navigate to additional results." : ""}`;
}
return `${summary}\n\n${knowledgeSections}${paginationInfo}`;
}
}
/**
* Create a human-readable formatted response for the atlas_knowledge_list tool
*
* @param data The structured knowledge query response data
* @param isError Whether this response represents an error condition
* @returns Formatted MCP tool response with appropriate structure
*/
export function formatKnowledgeListResponse(data: any, isError = false): any {
const formatter = new KnowledgeListFormatter();
const formattedText = formatter.format(data as KnowledgeListResponse); // Assuming data is KnowledgeListResponse
return createToolResponse(formattedText, isError);
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_knowledge_list/listKnowledge.ts:
--------------------------------------------------------------------------------
```typescript
import { McpError } from "../../../types/errors.js";
import { BaseErrorCode } from "../../../types/errors.js";
import { logger, requestContextService } from "../../../utils/index.js"; // Import requestContextService
import {
KnowledgeService,
ProjectService,
} from "../../../services/neo4j/index.js";
import {
KnowledgeItem,
KnowledgeListRequest,
KnowledgeListResponse,
} from "./types.js";
/**
* Retrieve and filter knowledge items based on specified criteria
*
* @param request The knowledge query parameters including filters and pagination controls
* @returns Promise resolving to structured knowledge items
*/
export async function listKnowledge(
request: KnowledgeListRequest,
): Promise<KnowledgeListResponse> {
const reqContext = requestContextService.createRequestContext({
operation: "listKnowledge",
requestParams: request,
});
try {
const { projectId, tags, domain, search, page = 1, limit = 20 } = request;
// Parameter validation
if (!projectId) {
throw new McpError(
BaseErrorCode.VALIDATION_ERROR,
"Project ID is required to list knowledge items",
);
}
// Verify that the project exists
const project = await ProjectService.getProjectById(projectId);
if (!project) {
throw new McpError(
BaseErrorCode.NOT_FOUND,
`Project with ID ${projectId} not found`,
);
}
// Sanitize pagination parameters
const validatedPage = Math.max(1, page);
const validatedLimit = Math.min(Math.max(1, limit), 100);
// Get knowledge items with filters
const knowledgeResult = await KnowledgeService.getKnowledge({
projectId,
tags,
domain,
search,
page: validatedPage,
limit: validatedLimit,
});
// Process knowledge items to ensure consistent structure
const knowledgeItems: KnowledgeItem[] = knowledgeResult.data.map((item) => {
// Handle Neo4j record structure which may include properties field
const rawItem = item as any;
const properties = rawItem.properties || rawItem;
return {
id: properties.id,
projectId: properties.projectId,
text: properties.text,
tags: properties.tags || [],
domain: properties.domain,
citations: properties.citations || [],
createdAt: properties.createdAt,
updatedAt: properties.updatedAt,
projectName: project.name, // Include project name for context
};
});
// Construct the response
const response: KnowledgeListResponse = {
knowledge: knowledgeItems,
total: knowledgeResult.total,
page: validatedPage,
limit: validatedLimit,
totalPages: knowledgeResult.totalPages,
};
logger.info("Knowledge query executed successfully", {
...reqContext,
projectId,
count: knowledgeItems.length,
total: knowledgeResult.total,
hasTags: !!tags,
hasDomain: !!domain,
hasSearch: !!search,
});
return response;
} catch (error) {
logger.error("Knowledge query execution failed", error as Error, {
...reqContext,
detail: (error as Error).message,
});
if (error instanceof McpError) {
throw error;
}
throw new McpError(
BaseErrorCode.INTERNAL_ERROR,
`Failed to retrieve knowledge items: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "atlas-mcp-server",
"version": "2.8.15",
"description": "ATLAS (Adaptive Task & Logic Automation System): An MCP server enabling LLM agents to manage projects, tasks, and knowledge via a Neo4j-backed, three-tier architecture. Facilitates complex workflow automation and project management through LLM Agents.",
"type": "module",
"main": "dist/index.js",
"exports": "./dist/index.js",
"files": [
"dist"
],
"bin": {
"atlas-mcp-server": "dist/index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/cyanheads/atlas-mcp-server.git"
},
"bugs": {
"url": "https://github.com/cyanheads/atlas-mcp-server/issues"
},
"homepage": "https://github.com/cyanheads/atlas-mcp-server#readme",
"scripts": {
"build": "tsc && node --loader ts-node/esm scripts/make-executable.ts dist/index.js",
"db:backup": "node --loader ts-node/esm src/services/neo4j/backupRestoreService/scripts/db-backup.ts",
"db:import": "node --loader ts-node/esm src/services/neo4j/backupRestoreService/scripts/db-import.ts",
"dev": "tsc -w",
"docker:down": "docker-compose down",
"docker:logs": "docker-compose logs -f",
"docker:up": "docker-compose up -d",
"docs:generate": "typedoc",
"fetch-spec": "ts-node --esm scripts/fetch-openapi-spec.ts",
"format": "prettier --write \"**/*.{ts,js,json,md,html,css}\"",
"inspector": "mcp-inspector --config mcp.json --server atlas-mcp-server",
"rebuild": "ts-node --esm scripts/clean.ts && npm run build",
"start": "node dist/index.js",
"start:http": "MCP_LOG_LEVEL=debug MCP_TRANSPORT_TYPE=http node dist/index.js",
"start:stdio": "MCP_LOG_LEVEL=debug MCP_TRANSPORT_TYPE=stdio node dist/index.js",
"tree": "ts-node --esm scripts/tree.ts",
"webui": "npx serve src/webui -l 8000"
},
"keywords": [
"ai-integration",
"agent",
"atlas",
"authentication",
"automation",
"client-server",
"collaboration",
"dependency-tracking",
"graph-database",
"http",
"jwt",
"knowledge-management",
"llm",
"llm-agent",
"mcp",
"model-context-protocol",
"model-context-protocol-server",
"neo4j",
"project-management",
"server",
"task-management",
"three-tier-architecture",
"typescript",
"unified-search",
"workflow-automation"
],
"author": "cyanheads <[email protected]> (https://github.com/cyanheads/atlas-mcp-server#readme)",
"license": "Apache-2.0",
"engines": {
"node": ">=16.0.0"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.1",
"@types/node": "^22.15.29",
"@types/node-cron": "^3.0.11",
"chrono-node": "2.8.0",
"commander": "^14.0.0",
"cross-env": "^7.0.3",
"date-fns": "^4.1.0",
"dotenv": "^16.5.0",
"express": "^5.1.0",
"fuzzysort": "^3.1.0",
"ignore": "^7.0.5",
"jsonwebtoken": "^9.0.2",
"nanoid": "^5.1.5",
"neo4j-driver": "^5.28.1",
"node-cron": "^4.1.0",
"node-schedule": "^2.1.1",
"openai": "^5.1.1",
"partial-json": "^0.1.7",
"sanitize-html": "^2.17.0",
"tiktoken": "^1.0.21",
"validator": "^13.15.15",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0",
"yargs": "^18.0.0",
"zod": "^3.25.51"
},
"devDependencies": {
"@types/express": "^5.0.2",
"@types/js-yaml": "^4.0.9",
"@types/jsonwebtoken": "^9.0.9",
"@types/node-schedule": "^2.1.7",
"@types/sanitize-html": "^2.16.0",
"@types/validator": "^13.15.1",
"axios": "^1.9.0",
"js-yaml": "^4.1.0",
"prettier": "^3.5.3",
"ts-node": "^10.9.2",
"typedoc": "^0.28.5",
"typescript": "^5.8.3"
}
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_task_delete/responseFormat.ts:
--------------------------------------------------------------------------------
```typescript
import { createToolResponse } from "../../../types/mcp.js"; // Import the new response creator
/**
* Defines a generic interface for formatting data into a string.
*/
interface ResponseFormatter<T> {
format(data: T): string;
}
/**
* Interface for single task deletion response
*/
interface SingleTaskDeleteResponse {
id: string;
success: boolean;
message: string;
}
/**
* Interface for bulk task deletion response
*/
interface BulkTaskDeleteResponse {
success: boolean;
message: string;
deleted: string[];
errors: {
taskId: string;
error: {
code: string;
message: string;
details?: any;
};
}[];
}
/**
* Formatter for individual task removal responses
*/
export class SingleTaskDeleteFormatter
implements ResponseFormatter<SingleTaskDeleteResponse>
{
format(data: SingleTaskDeleteResponse): string {
return (
`Task Removal\n\n` +
`Result: ${data.success ? "✅ Success" : "❌ Failed"}\n` +
`Task ID: ${data.id}\n` +
`Message: ${data.message}\n`
);
}
}
/**
* Formatter for batch task removal responses
*/
export class BulkTaskDeleteFormatter
implements ResponseFormatter<BulkTaskDeleteResponse>
{
format(data: BulkTaskDeleteResponse): string {
const { success, message, deleted, errors } = data;
const summary =
`Task Cleanup Operation\n\n` +
`Status: ${success && errors.length === 0 ? "✅ Complete Success" : errors.length > 0 ? "⚠️ Partial Success / Errors" : "✅ Success (No items or no errors)"}\n` +
`Summary: ${message}\n` +
`Removed: ${deleted.length} task(s)\n` +
`Errors: ${errors.length} error(s)\n`;
let deletedSection = "";
if (deleted.length > 0) {
deletedSection = `\n--- Removed Tasks (${deleted.length}) ---\n\n`;
deletedSection += deleted.map((id) => `- ${id}`).join("\n");
}
let errorsSection = "";
if (errors.length > 0) {
errorsSection = `\n--- Errors Encountered (${errors.length}) ---\n\n`;
errorsSection += errors
.map((errorItem, index) => {
return (
`${index + 1}. Failed to remove ID: ${errorItem.taskId}\n` +
` Error Code: ${errorItem.error.code}\n` +
` Message: ${errorItem.error.message}` +
(errorItem.error.details
? `\n Details: ${JSON.stringify(errorItem.error.details)}`
: "")
);
})
.join("\n\n");
}
return `${summary}${deletedSection}${errorsSection}`.trim();
}
}
/**
* Create a human-readable formatted response for the atlas_task_delete tool
*
* @param data The structured task removal operation results (SingleTaskDeleteResponse or BulkTaskDeleteResponse)
* @param isError This parameter is effectively ignored as success is determined from data.success. Kept for signature consistency if needed.
* @returns Formatted MCP tool response with appropriate structure
*/
export function formatTaskDeleteResponse(data: any, isError = false): any {
const isBulkResponse =
data.hasOwnProperty("deleted") &&
Array.isArray(data.deleted) &&
data.hasOwnProperty("errors");
let formattedText: string;
let finalIsError: boolean;
if (isBulkResponse) {
const formatter = new BulkTaskDeleteFormatter();
const bulkData = data as BulkTaskDeleteResponse;
formattedText = formatter.format(bulkData);
finalIsError = !bulkData.success || bulkData.errors.length > 0;
} else {
const formatter = new SingleTaskDeleteFormatter();
const singleData = data as SingleTaskDeleteResponse;
formattedText = formatter.format(singleData);
finalIsError = !singleData.success;
}
return createToolResponse(formattedText, finalIsError);
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_project_delete/responseFormat.ts:
--------------------------------------------------------------------------------
```typescript
import { createToolResponse } from "../../../types/mcp.js"; // Import the new response creator
/**
* Defines a generic interface for formatting data into a string.
*/
interface ResponseFormatter<T> {
format(data: T): string;
}
/**
* Interface for single project deletion response
*/
interface SingleProjectDeleteResponse {
id: string;
success: boolean;
message: string;
}
/**
* Interface for bulk project deletion response
*/
interface BulkProjectDeleteResponse {
success: boolean;
message: string;
deleted: string[];
errors: {
projectId: string;
error: {
code: string;
message: string;
details?: any;
};
}[];
}
/**
* Formatter for individual project removal responses
*/
export class SingleProjectDeleteFormatter
implements ResponseFormatter<SingleProjectDeleteResponse>
{
format(data: SingleProjectDeleteResponse): string {
return (
`Project Removal\n\n` +
`Result: ${data.success ? "✅ Success" : "❌ Failed"}\n` +
`Project ID: ${data.id}\n` +
`Message: ${data.message}\n`
);
}
}
/**
* Formatter for batch project removal responses
*/
export class BulkProjectDeleteFormatter
implements ResponseFormatter<BulkProjectDeleteResponse>
{
format(data: BulkProjectDeleteResponse): string {
const { success, message, deleted, errors } = data;
const summary =
`Project Cleanup Operation\n\n` +
`Status: ${success && errors.length === 0 ? "✅ Complete Success" : errors.length > 0 ? "⚠️ Partial Success / Errors" : "✅ Success (No items or no errors)"}\n` +
`Summary: ${message}\n` +
`Removed: ${deleted.length} project(s)\n` +
`Errors: ${errors.length} error(s)\n`;
let deletedSection = "";
if (deleted.length > 0) {
deletedSection = `\n--- Removed Projects (${deleted.length}) ---\n\n`;
deletedSection += deleted.map((id) => `- ${id}`).join("\n");
}
let errorsSection = "";
if (errors.length > 0) {
errorsSection = `\n--- Errors Encountered (${errors.length}) ---\n\n`;
errorsSection += errors
.map((errorItem, index) => {
return (
`${index + 1}. Failed to remove ID: ${errorItem.projectId}\n` +
` Error Code: ${errorItem.error.code}\n` +
` Message: ${errorItem.error.message}` +
(errorItem.error.details
? `\n Details: ${JSON.stringify(errorItem.error.details)}`
: "")
);
})
.join("\n\n");
}
return `${summary}${deletedSection}${errorsSection}`.trim();
}
}
/**
* Create a human-readable formatted response for the atlas_project_delete tool
*
* @param data The structured project removal operation results (SingleProjectDeleteResponse or BulkProjectDeleteResponse)
* @param isError This parameter is effectively ignored as success is determined from data.success. Kept for signature consistency if needed.
* @returns Formatted MCP tool response with appropriate structure
*/
export function formatProjectDeleteResponse(data: any, isError = false): any {
const isBulkResponse =
data.hasOwnProperty("deleted") &&
Array.isArray(data.deleted) &&
data.hasOwnProperty("errors");
let formattedText: string;
let finalIsError: boolean;
if (isBulkResponse) {
const formatter = new BulkProjectDeleteFormatter();
const bulkData = data as BulkProjectDeleteResponse;
formattedText = formatter.format(bulkData);
finalIsError = !bulkData.success || bulkData.errors.length > 0;
} else {
const formatter = new SingleProjectDeleteFormatter();
const singleData = data as SingleProjectDeleteResponse;
formattedText = formatter.format(singleData);
finalIsError = !singleData.success;
}
return createToolResponse(formattedText, finalIsError);
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_knowledge_delete/responseFormat.ts:
--------------------------------------------------------------------------------
```typescript
import { createToolResponse } from "../../../types/mcp.js"; // Import the new response creator
/**
* Defines a generic interface for formatting data into a string.
*/
interface ResponseFormatter<T> {
format(data: T): string;
}
/**
* Interface for single knowledge item deletion response
*/
interface SingleKnowledgeDeleteResponse {
id: string;
success: boolean;
message: string;
}
/**
* Interface for bulk knowledge deletion response
*/
interface BulkKnowledgeDeleteResponse {
success: boolean;
message: string;
deleted: string[];
errors: {
knowledgeId: string;
error: {
code: string;
message: string;
details?: any;
};
}[];
}
/**
* Formatter for individual knowledge item removal responses
*/
export class SingleKnowledgeDeleteFormatter
implements ResponseFormatter<SingleKnowledgeDeleteResponse>
{
format(data: SingleKnowledgeDeleteResponse): string {
return (
`Knowledge Item Removal\n\n` +
`Result: ${data.success ? "✅ Success" : "❌ Failed"}\n` +
`Knowledge ID: ${data.id}\n` +
`Message: ${data.message}\n`
);
}
}
/**
* Formatter for batch knowledge item removal responses
*/
export class BulkKnowledgeDeleteFormatter
implements ResponseFormatter<BulkKnowledgeDeleteResponse>
{
format(data: BulkKnowledgeDeleteResponse): string {
const { success, message, deleted, errors } = data;
const summary =
`Knowledge Cleanup Operation\n\n` +
`Status: ${success && errors.length === 0 ? "✅ Complete Success" : errors.length > 0 ? "⚠️ Partial Success / Errors" : "✅ Success (No items or no errors)"}\n` +
`Summary: ${message}\n` +
`Removed: ${deleted.length} knowledge item(s)\n` +
`Errors: ${errors.length} error(s)\n`;
let deletedSection = "";
if (deleted.length > 0) {
deletedSection = `\n--- Removed Knowledge Items (${deleted.length}) ---\n\n`;
deletedSection += deleted.map((id) => `- ${id}`).join("\n");
}
let errorsSection = "";
if (errors.length > 0) {
errorsSection = `\n--- Errors Encountered (${errors.length}) ---\n\n`;
errorsSection += errors
.map((errorItem, index) => {
return (
`${index + 1}. Failed to remove ID: ${errorItem.knowledgeId}\n` +
` Error Code: ${errorItem.error.code}\n` +
` Message: ${errorItem.error.message}` +
(errorItem.error.details
? `\n Details: ${JSON.stringify(errorItem.error.details)}`
: "")
);
})
.join("\n\n");
}
return `${summary}${deletedSection}${errorsSection}`.trim();
}
}
/**
* Create a human-readable formatted response for the atlas_knowledge_delete tool
*
* @param data The structured knowledge removal operation results (SingleKnowledgeDeleteResponse or BulkKnowledgeDeleteResponse)
* @param isError This parameter is effectively ignored as success is determined from data.success. Kept for signature consistency if needed.
* @returns Formatted MCP tool response with appropriate structure
*/
export function formatKnowledgeDeleteResponse(data: any, isError = false): any {
const isBulkResponse =
data.hasOwnProperty("deleted") &&
Array.isArray(data.deleted) &&
data.hasOwnProperty("errors");
let formattedText: string;
let finalIsError: boolean;
if (isBulkResponse) {
const formatter = new BulkKnowledgeDeleteFormatter();
const bulkData = data as BulkKnowledgeDeleteResponse;
formattedText = formatter.format(bulkData);
finalIsError = !bulkData.success || bulkData.errors.length > 0;
} else {
const formatter = new SingleKnowledgeDeleteFormatter();
const singleData = data as SingleKnowledgeDeleteResponse;
formattedText = formatter.format(singleData);
finalIsError = !singleData.success;
}
return createToolResponse(formattedText, finalIsError);
}
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
startCommand:
type: stdioOrHttp # Allow user to choose transport type
configSchema:
type: object
properties:
NEO4J_URI:
type: string
description: "Neo4j connection URI (e.g., bolt://localhost:7687)"
default: "bolt://localhost:7687"
NEO4J_USER:
type: string
description: "Neo4j username"
default: "neo4j"
NEO4J_PASSWORD:
type: string
description: "Neo4j password"
format: "password" # For secure input in UIs
default: "password2"
LOG_LEVEL:
type: string
description: "Log level for the server"
enum:
[
"emerg",
"alert",
"crit",
"error",
"warning",
"notice",
"info",
"debug",
]
default: "info"
NODE_ENV:
type: string
description: "Node environment"
enum: ["development", "production"]
default: "development"
MCP_HTTP_HOST:
type: string
description: "Host for HTTP transport (if selected)"
default: "127.0.0.1"
MCP_HTTP_PORT:
type: integer # Changed to integer for better validation
description: "Port for HTTP transport (if selected)"
default: 3010
MCP_ALLOWED_ORIGINS:
type: string # Comma-separated string
description: "Optional: Comma-separated list of allowed origins for HTTP CORS (e.g., http://localhost:3000,https://your-client.com)"
default: ""
MCP_AUTH_SECRET_KEY:
type: string
description: "Optional: Secret key for JWT authentication if HTTP transport is used (min 32 chars)"
format: "password" # For secure input in UIs
default: ""
MCP_RATE_LIMIT_WINDOW_MS:
type: integer
description: "Rate limit window in milliseconds for HTTP transport"
default: 60000
MCP_RATE_LIMIT_MAX_REQUESTS:
type: integer
description: "Max requests per window per IP for HTTP transport"
default: 100
BACKUP_MAX_COUNT:
type: integer
description: "Maximum number of backup sets to keep"
default: 10
BACKUP_FILE_DIR:
type: string
description: "Directory where backup files will be stored (relative to project root)"
default: "./atlas-backups"
LOGS_DIR:
type: string
description: "Directory where log files will be stored (relative to project root)"
default: "./logs"
required: # Define required fields based on README defaults and common usage
- NEO4J_URI
- NEO4J_USER
- NEO4J_PASSWORD
- LOG_LEVEL
- NODE_ENV
commandFunction: |
(config, transportType) => {
const env = {
NEO4J_URI: config.NEO4J_URI,
NEO4J_USER: config.NEO4J_USER,
NEO4J_PASSWORD: config.NEO4J_PASSWORD,
LOG_LEVEL: config.LOG_LEVEL,
NODE_ENV: config.NODE_ENV,
MCP_TRANSPORT_TYPE: transportType, // 'stdio' or 'http'
MCP_HTTP_HOST: config.MCP_HTTP_HOST,
MCP_HTTP_PORT: String(config.MCP_HTTP_PORT), // Ensure it's a string for env var
MCP_ALLOWED_ORIGINS: config.MCP_ALLOWED_ORIGINS,
MCP_AUTH_SECRET_KEY: config.MCP_AUTH_SECRET_KEY,
MCP_RATE_LIMIT_WINDOW_MS: String(config.MCP_RATE_LIMIT_WINDOW_MS),
MCP_RATE_LIMIT_MAX_REQUESTS: String(config.MCP_RATE_LIMIT_MAX_REQUESTS),
BACKUP_MAX_COUNT: String(config.BACKUP_MAX_COUNT),
BACKUP_FILE_DIR: config.BACKUP_FILE_DIR,
LOGS_DIR: config.LOGS_DIR,
};
// Remove undefined or empty string values to avoid passing them as env vars
for (const key in env) {
if (env[key] === undefined || env[key] === "") {
delete env[key];
}
}
return {
"command": "node",
"args": ["dist/index.js"],
"env": env
};
}
```
--------------------------------------------------------------------------------
/src/utils/internal/requestContext.ts:
--------------------------------------------------------------------------------
```typescript
/**
* @fileoverview Utilities for creating and managing request contexts.
* A request context is an object carrying a unique ID, timestamp, and other
* relevant data for logging, tracing, and processing. It also defines
* configuration and operational context structures.
* @module src/utils/internal/requestContext
*/
import { generateUUID } from "../index.js";
import { logger } from "./logger.js";
/**
* Defines the core structure for context information associated with a request or operation.
* This is fundamental for logging, tracing, and passing operational data.
*/
export interface RequestContext {
/**
* Unique ID for the context instance.
* Used for log correlation and request tracing.
*/
requestId: string;
/**
* ISO 8601 timestamp indicating when the context was created.
*/
timestamp: string;
/**
* Allows arbitrary key-value pairs for specific context needs.
* Using `unknown` promotes type-safe access.
* Consumers must type-check/assert when accessing extended properties.
*/
[key: string]: unknown;
}
/**
* Configuration for the {@link requestContextService}.
* Allows for future extensibility of service-wide settings.
*/
export interface ContextConfig {
/** Custom configuration properties. Allows for arbitrary key-value pairs. */
[key: string]: unknown;
}
/**
* Represents a broader context for a specific operation or task.
* It can optionally include a base {@link RequestContext} and other custom properties
* relevant to the operation.
*/
export interface OperationContext {
/** Optional base request context data, adhering to the `RequestContext` structure. */
requestContext?: RequestContext;
/** Allows for additional, custom properties specific to the operation. */
[key: string]: unknown;
}
/**
* Singleton-like service object for managing request context operations.
* @private
*/
const requestContextServiceInstance = {
/**
* Internal configuration store for the service.
*/
config: {} as ContextConfig,
/**
* Configures the request context service with new settings.
* Merges the provided partial configuration with existing settings.
*
* @param config - A partial `ContextConfig` object containing settings to update or add.
* @returns A shallow copy of the newly updated configuration.
*/
configure(config: Partial<ContextConfig>): ContextConfig {
this.config = {
...this.config,
...config,
};
const logContext = this.createRequestContext({
operation: "RequestContextService.configure",
newConfigState: { ...this.config },
});
logger.debug("RequestContextService configuration updated", logContext);
return { ...this.config };
},
/**
* Retrieves a shallow copy of the current service configuration.
* This prevents direct mutation of the internal configuration state.
*
* @returns A shallow copy of the current `ContextConfig`.
*/
getConfig(): ContextConfig {
return { ...this.config };
},
/**
* Creates a new {@link RequestContext} instance.
* Each context is assigned a unique `requestId` (UUID) and a current `timestamp` (ISO 8601).
* Additional custom properties can be merged into the context.
*
* @param additionalContext - An optional record of key-value pairs to be
* included in the created request context.
* @returns A new `RequestContext` object.
*/
createRequestContext(
additionalContext: Record<string, unknown> = {},
): RequestContext {
const requestId = generateUUID();
const timestamp = new Date().toISOString();
const context: RequestContext = {
requestId,
timestamp,
...additionalContext,
};
return context;
},
};
/**
* Primary export for request context functionalities.
* This service provides methods to create and manage {@link RequestContext} instances,
* which are essential for logging, tracing, and correlating operations.
*/
export const requestContextService = requestContextServiceInstance;
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_task_list/responseFormat.ts:
--------------------------------------------------------------------------------
```typescript
import { Neo4jTask } from "../../../services/neo4j/types.js";
import { createToolResponse, McpToolResponse } from "../../../types/mcp.js";
import { TaskListResponse } from "./types.js";
/**
* Formatter for task list responses
*/
class TaskListFormatter {
format(data: TaskListResponse): string {
// Destructure, providing default empty array for tasks if undefined/null
const { tasks = [], total, page, limit, totalPages } = data;
// Add an explicit check if tasks is actually an array after destructuring (extra safety)
if (!Array.isArray(tasks)) {
// Log error or return a specific error message
return "Error: Invalid task data received.";
}
// Create a summary section with pagination info
const summary =
`Task List\n\n` +
`Found ${total} task(s)\n` +
`Page ${page} of ${totalPages} (${limit} per page)\n`;
// Early return if no tasks found
if (tasks.length === 0) {
return `${summary}\nNo tasks found matching the specified criteria.`;
}
// Create a table of tasks
let tasksSection = "Tasks:\n\n";
tasksSection += tasks
.map((taskData, index) => {
// Rename to avoid conflict
// Add safety check for null/undefined task data
if (!taskData) {
// Log a warning or handle appropriately if needed, here just skip
return null; // Return null to filter out later
}
// Cast the task data to include the assignedToUserId property
const task = taskData as Neo4jTask & {
assignedToUserId: string | null;
};
const statusEmoji = this.getStatusEmoji(task.status);
const priorityIndicator = this.getPriorityIndicator(task.priority);
// Use assignedToUserId from the task object
const assignedToLine = task.assignedToUserId
? ` Assigned To: ${task.assignedToUserId}\n`
: "";
return (
`${index + 1}. ${statusEmoji} ${priorityIndicator} ${task.title}\n` +
` ID: ${task.id}\n` +
` Status: ${task.status}\n` +
` Priority: ${task.priority}\n` +
assignedToLine + // Use the updated assignee line
// Safely check if tags is an array and has items before joining
(Array.isArray(task.tags) && task.tags.length > 0
? ` Tags: ${task.tags.join(", ")}\n`
: "") +
` Type: ${task.taskType}\n` +
` Created: ${new Date(task.createdAt).toLocaleString()}\n`
);
})
.filter(Boolean) // Filter out any null entries from the map
.join("\n");
// Add help text for pagination
let paginationHelp = "";
if (totalPages > 1) {
paginationHelp = `\nTo view more tasks, use 'page' parameter (current: ${page}, total pages: ${totalPages}).`;
}
return `${summary}\n${tasksSection}${paginationHelp}`;
}
/**
* Get an emoji indicator for the task status
*/
private getStatusEmoji(status: string): string {
switch (status) {
case "backlog":
return "📋";
case "todo":
return "📝";
case "in_progress":
return "🔄";
case "completed":
return "✅";
default:
return "❓";
}
}
/**
* Get a visual indicator for the priority level
*/
private getPriorityIndicator(priority: string): string {
switch (priority) {
case "critical":
return "[!!!]";
case "high":
return "[!!]";
case "medium":
return "[!]";
case "low":
return "[-]";
default:
return "[?]";
}
}
}
/**
* Create a formatted, human-readable response for the atlas_task_list tool
*
* @param data The task list response data
* @param isError Whether this response represents an error condition
* @returns Formatted MCP tool response with appropriate structure
*/
export function formatTaskListResponse(
data: TaskListResponse,
isError = false,
): McpToolResponse {
const formatter = new TaskListFormatter();
const formattedText = formatter.format(data);
return createToolResponse(formattedText, isError);
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_project_list/types.ts:
--------------------------------------------------------------------------------
```typescript
import { ProjectStatus } from "../../../types/mcp.js";
import { Neo4jProject } from "../../../services/neo4j/types.js";
/**
* Query parameters for retrieving and filtering projects
*/
export interface ProjectListRequest {
/** Query mode - 'all' for collection retrieval, 'details' for specific entity */
mode?: "all" | "details";
/** Target project identifier for detailed retrieval (required for mode='details') */
id?: string;
/** Pagination control - page number (Default: 1) */
page?: number;
/** Pagination control - results per page, max 100 (Default: 20) */
limit?: number;
/** Flag to include associated knowledge resources (Default: false) */
includeKnowledge?: boolean;
/** Flag to include associated task entities (Default: false) */
includeTasks?: boolean;
/** Filter selector for project classification/category */
taskType?: string;
/** Filter selector for project lifecycle state */
status?:
| "active"
| "pending"
| "in-progress"
| "completed"
| "archived"
| ("active" | "pending" | "in-progress" | "completed" | "archived")[];
}
/**
* Response structure for project queries
*/
export interface ProjectListResponse {
/** Collection of projects matching search criteria */
projects: Project[];
/** Total record count matching criteria (pre-pagination) */
total: number;
/** Current pagination position */
page: number;
/** Pagination size setting */
limit: number;
/** Total available pages for the current query */
totalPages: number;
}
/**
* Project entity structure for API responses, mirroring Neo4jProject structure
*/
export interface Project {
/** Unique project identifier */
id: string;
/** Project title */
name: string;
/** Project scope and objectives */
description: string;
/** Current lifecycle state */
status: string;
/** Project classification category */
taskType: string;
/** Success criteria and definition of done */
completionRequirements: string;
/** Expected deliverable specification */
outputFormat: string;
/** Creation timestamp (ISO format) */
createdAt: string;
/** Last modification timestamp (ISO format) */
updatedAt: string;
/** Parsed reference materials with titles */
urls?: Array<{ title: string; url: string }>;
/** Associated knowledge resources (conditional inclusion) */
knowledge?: Knowledge[]; // Note: This structure is simplified for the tool response
/** Associated task entities (conditional inclusion) */
tasks?: Task[];
}
/**
* Knowledge resource model for abbreviated references, mirroring Neo4jKnowledge structure
*/
export interface Knowledge {
/** Unique knowledge resource identifier */
id: string;
/** ID of the parent project this knowledge belongs to */
projectId: string;
/** Knowledge content */
text: string;
/** Taxonomic classification labels */
tags?: string[];
/** Primary knowledge domain/category */
domain: string;
/** Resource creation timestamp (ISO format) */
createdAt: string;
/** Last modification timestamp (ISO format) */
updatedAt: string;
/** Reference sources supporting this knowledge (URLs, DOIs, etc.) */
citations?: string[];
}
/**
* Task entity model for abbreviated references, mirroring Neo4jTask structure
*/
export interface Task {
/** Unique task identifier */
id: string;
/** ID of the parent project this task belongs to */
projectId: string;
/** Task description */
title: string;
/** Current workflow state */
status: string;
/** Task importance classification */
priority: string;
/** Task creation timestamp (ISO format) */
createdAt: string;
/** Last modification timestamp (ISO format) */
updatedAt: string;
/** ID of entity responsible for task completion */
assignedTo?: string;
/** Reference materials */
urls?: Array<{ title: string; url: string }>;
/** Specific, measurable criteria that indicate task completion */
completionRequirements: string;
/** Required format specification for task deliverables */
outputFormat: string;
/** Classification of task purpose */
taskType: string;
/** Organizational labels */
tags?: string[];
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_knowledge_add/types.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from "zod";
import {
McpToolResponse,
KnowledgeDomain,
createKnowledgeDomainEnum,
ResponseFormat,
createResponseFormatEnum,
} from "../../../types/mcp.js";
export const KnowledgeSchema = z.object({
id: z.string().optional().describe("Optional client-generated knowledge ID"),
projectId: z
.string()
.describe("ID of the parent project this knowledge belongs to"),
text: z
.string()
.describe(
"Main content of the knowledge item (can be structured or unstructured)",
),
tags: z
.array(z.string())
.optional()
.describe("Categorical labels for organization and filtering"),
domain: createKnowledgeDomainEnum()
.or(z.string())
.describe("Primary knowledge area or discipline"),
citations: z
.array(z.string())
.optional()
.describe(
"Array of reference sources supporting this knowledge (URLs, DOIs, etc.)",
),
});
const SingleKnowledgeSchema = z
.object({
mode: z.literal("single"),
id: z.string().optional(),
projectId: z.string(),
text: z.string(),
tags: z.array(z.string()).optional(),
domain: createKnowledgeDomainEnum().or(z.string()),
citations: z.array(z.string()).optional(),
responseFormat: createResponseFormatEnum()
.optional()
.default(ResponseFormat.FORMATTED)
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
})
.describe(
"Adds a single knowledge item with comprehensive details and metadata",
);
const BulkKnowledgeSchema = z
.object({
mode: z.literal("bulk"),
knowledge: z
.array(KnowledgeSchema)
.min(1)
.max(100)
.describe("Array of knowledge objects with the above fields"),
responseFormat: createResponseFormatEnum()
.optional()
.default(ResponseFormat.FORMATTED)
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
})
.describe("Add multiple knowledge items in a single efficient transaction");
// Schema shapes for tool registration
export const AtlasKnowledgeAddSchemaShape = {
mode: z
.enum(["single", "bulk"])
.describe(
"Operation mode - 'single' for creating one detailed knowledge item, 'bulk' for efficiently adding multiple related knowledge items in a single transaction",
),
id: z
.string()
.optional()
.describe(
"Optional client-generated knowledge ID for consistent cross-referencing and retrieval",
),
projectId: z
.string()
.optional()
.describe(
"ID of the parent project this knowledge belongs to, establishing project-knowledge association (required for mode='single')",
),
text: z
.string()
.optional()
.describe(
"Main content of the knowledge item containing insights, findings, or reference information (required for mode='single')",
),
tags: z
.array(z.string())
.optional()
.describe(
"Array of categorical labels for knowledge organization, thematic grouping, and advanced filtering capabilities",
),
domain: createKnowledgeDomainEnum()
.or(z.string())
.optional()
.describe(
"Primary knowledge area or discipline for high-level categorization and domain-specific searching (required for mode='single')",
),
citations: z
.array(z.string())
.optional()
.describe(
"Array of reference sources supporting this knowledge for validation and additional context (URLs, DOIs, papers, etc.)",
),
knowledge: z
.array(KnowledgeSchema)
.min(1)
.max(100)
.optional()
.describe(
"Array of complete knowledge definition objects to create in a single transaction (supports 1-100 items, required for mode='bulk')",
),
responseFormat: createResponseFormatEnum()
.optional()
.describe(
"Desired response format: 'formatted' (default string) or 'json' (raw object)",
),
} as const;
// Schema for validation
export const AtlasKnowledgeAddSchema = z.discriminatedUnion("mode", [
SingleKnowledgeSchema,
BulkKnowledgeSchema,
]);
export type AtlasKnowledgeAddInput = z.infer<typeof AtlasKnowledgeAddSchema>;
export type KnowledgeInput = z.infer<typeof KnowledgeSchema>;
export type AtlasKnowledgeAddResponse = McpToolResponse;
```
--------------------------------------------------------------------------------
/scripts/make-executable.ts:
--------------------------------------------------------------------------------
```typescript
#!/usr/bin/env node
/**
* @fileoverview Utility script to make files executable (chmod +x) on Unix-like systems.
* @module scripts/make-executable
* On Windows, this script does nothing but exits successfully.
* Useful for CLI applications where built output needs executable permissions.
* Default target (if no args): dist/index.js.
* Ensures output paths are within the project directory for security.
*
* @example
* // Add to package.json build script:
* // "build": "tsc && ts-node --esm scripts/make-executable.ts dist/index.js"
*
* @example
* // Run directly with custom files:
* // ts-node --esm scripts/make-executable.ts path/to/script1 path/to/script2
*/
import fs from "fs/promises";
import os from "os";
import path from "path";
const isUnix = os.platform() !== "win32";
const projectRoot = process.cwd();
const EXECUTABLE_MODE = 0o755; // rwxr-xr-x
/**
* Represents the result of an attempt to make a file executable.
* @property file - The relative path of the file targeted.
* @property status - The outcome of the operation ('success', 'error', or 'skipped').
* @property reason - If status is 'error' or 'skipped', an explanation.
*/
interface ExecutableResult {
file: string;
status: "success" | "error" | "skipped";
reason?: string;
}
/**
* Main function to make specified files executable.
* Skips operation on Windows. Processes command-line arguments for target files
* or defaults to 'dist/index.js'. Reports status for each file.
*/
const makeExecutable = async (): Promise<void> => {
try {
const targetFiles: string[] =
process.argv.slice(2).length > 0
? process.argv.slice(2)
: ["dist/index.js"];
if (!isUnix) {
console.log(
"Skipping chmod operation: Script is running on Windows (not applicable).",
);
return;
}
console.log(
`Attempting to make files executable: ${targetFiles.join(", ")}`,
);
const results = await Promise.allSettled(
targetFiles.map(async (targetFile): Promise<ExecutableResult> => {
const normalizedPath = path.resolve(projectRoot, targetFile);
if (
!normalizedPath.startsWith(projectRoot + path.sep) &&
normalizedPath !== projectRoot
) {
return {
file: targetFile,
status: "error",
reason: `Path resolves outside project boundary: ${normalizedPath}`,
};
}
try {
await fs.access(normalizedPath); // Check if file exists
await fs.chmod(normalizedPath, EXECUTABLE_MODE);
return { file: targetFile, status: "success" };
} catch (error) {
const err = error as NodeJS.ErrnoException;
if (err.code === "ENOENT") {
return {
file: targetFile,
status: "error",
reason: "File not found",
};
}
console.error(
`Error setting executable permission for ${targetFile}: ${err.message}`,
);
return { file: targetFile, status: "error", reason: err.message };
}
}),
);
let hasErrors = false;
results.forEach((result) => {
if (result.status === "fulfilled") {
const { file, status, reason } = result.value;
if (status === "success") {
console.log(`Successfully made executable: ${file}`);
} else if (status === "error") {
console.error(`Error for ${file}: ${reason}`);
hasErrors = true;
} else if (status === "skipped") {
// This status is not currently generated by the mapAsync logic but kept for future flexibility
console.warn(`Skipped ${file}: ${reason}`);
}
} else {
console.error(
`Unexpected failure for one of the files: ${result.reason}`,
);
hasErrors = true;
}
});
if (hasErrors) {
console.error(
"One or more files could not be made executable. Please check the errors above.",
);
// process.exit(1); // Uncomment to exit with error if any file fails
} else {
console.log("All targeted files processed successfully.");
}
} catch (error) {
console.error(
"A fatal error occurred during the make-executable script:",
error instanceof Error ? error.message : error,
);
process.exit(1);
}
};
makeExecutable();
```
--------------------------------------------------------------------------------
/src/types/mcp.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from "zod";
// Common response types
export interface McpContent {
[key: string]: unknown;
type: "text";
text: string;
}
export interface McpToolResponse {
[key: string]: unknown;
content: McpContent[];
_meta?: Record<string, unknown>;
isError?: boolean;
}
// Atlas Platform Enums
export const ProjectStatus = {
ACTIVE: "active",
PENDING: "pending",
IN_PROGRESS: "in-progress",
COMPLETED: "completed",
ARCHIVED: "archived",
} as const;
export const TaskStatus = {
BACKLOG: "backlog",
TODO: "todo",
IN_PROGRESS: "in-progress",
COMPLETED: "completed",
} as const;
export const PriorityLevel = {
LOW: "low",
MEDIUM: "medium",
HIGH: "high",
CRITICAL: "critical",
} as const;
export const TaskType = {
RESEARCH: "research",
GENERATION: "generation",
ANALYSIS: "analysis",
INTEGRATION: "integration",
} as const;
export const KnowledgeDomain = {
TECHNICAL: "technical",
BUSINESS: "business",
SCIENTIFIC: "scientific",
} as const;
// Atlas Platform response types
export interface ProjectResponse {
id: string;
name: string;
description: string;
status: string;
urls?: Array<{ title: string; url: string }>;
completionRequirements: string;
dependencies?: string[];
outputFormat: string;
taskType: string;
createdAt: string;
updatedAt: string;
}
export interface TaskResponse {
id: string;
projectId: string;
title: string;
description: string;
priority: string;
status: string;
assignedTo?: string;
urls?: Array<{ title: string; url: string }>;
tags?: string[];
completionRequirements: string;
dependencies?: string[];
outputFormat: string;
taskType: string;
createdAt: string;
updatedAt: string;
}
export interface KnowledgeResponse {
id: string;
projectId: string;
text: string;
tags?: string[];
domain: string;
citations?: string[];
createdAt: string;
updatedAt: string;
}
// Zod enum creators - these functions create Zod enums from our constant objects
export const createProjectStatusEnum = () => {
return z.enum(Object.values(ProjectStatus) as [string, ...string[]]);
};
export const createTaskStatusEnum = () => {
return z.enum(Object.values(TaskStatus) as [string, ...string[]]);
};
export const createPriorityLevelEnum = () => {
return z.enum(Object.values(PriorityLevel) as [string, ...string[]]);
};
export const createTaskTypeEnum = () => {
return z.enum(Object.values(TaskType) as [string, ...string[]]);
};
export const createKnowledgeDomainEnum = () => {
return z.enum(Object.values(KnowledgeDomain) as [string, ...string[]]);
};
export enum ResponseFormat {
FORMATTED = "formatted",
JSON = "json",
}
export function createResponseFormatEnum() {
return z.nativeEnum(ResponseFormat);
}
// Project-specific schemas
export const ProjectInputSchema = {
name: z.string().min(1),
description: z.string().optional(),
status: createProjectStatusEnum().default(ProjectStatus.ACTIVE),
} as const;
export const UpdateProjectInputSchema = {
id: z.string(),
updates: z.object(ProjectInputSchema).partial(),
} as const;
export const ProjectIdInputSchema = {
projectId: z.string(),
} as const;
// Resource response types
export interface ResourceContent {
[key: string]: unknown;
uri: string;
text: string;
mimeType?: string;
}
export interface ResourceResponse {
[key: string]: unknown;
contents: ResourceContent[];
_meta?: Record<string, unknown>;
}
// Prompt response types
export interface PromptMessageContent {
[key: string]: unknown;
type: "text";
text: string;
}
export interface PromptMessage {
[key: string]: unknown;
role: "user" | "assistant";
content: PromptMessageContent;
}
export interface PromptResponse {
[key: string]: unknown;
messages: PromptMessage[];
_meta?: Record<string, unknown>;
}
// Helper functions
export const createToolResponse = (
text: string,
isError?: boolean,
): McpToolResponse => ({
content: [
{
type: "text",
text,
_type: "text",
},
],
isError,
_type: "tool_response",
});
export const createResourceResponse = (
uri: string,
text: string,
mimeType?: string,
): ResourceResponse => ({
contents: [
{
uri,
text,
mimeType,
_type: "resource_content",
},
],
_type: "resource_response",
});
export const createPromptResponse = (
text: string,
role: "user" | "assistant" = "assistant",
): PromptResponse => ({
messages: [
{
role,
content: {
type: "text",
text,
_type: "prompt_content",
},
_type: "prompt_message",
},
],
_type: "prompt_response",
});
```
--------------------------------------------------------------------------------
/src/services/neo4j/types.ts:
--------------------------------------------------------------------------------
```typescript
/**
* Common type definitions for the Neo4j service
*/
/**
* Neo4j entity base interface
* Common properties for all Neo4j entities
*/
export interface Neo4jEntity {
id: string;
createdAt: string;
updatedAt: string;
}
/**
* Project entity in Neo4j
*/
export interface Neo4jProject extends Neo4jEntity {
name: string;
description: string;
status: string; // Allow any status value from ProjectStatus
urls?: Array<{ title: string; url: string }>;
completionRequirements: string;
outputFormat: string;
taskType: string;
}
/**
* Task entity in Neo4j
*/
export interface Neo4jTask extends Neo4jEntity {
projectId: string;
title: string;
description: string;
priority: string; // Allow any priority value from PriorityLevel
status: string; // Allow any status value from TaskStatus
// assignedTo is now represented by the ASSIGNED_TO relationship
urls?: Array<{ title: string; url: string }>;
tags?: string[];
completionRequirements: string;
outputFormat: string;
taskType: string;
}
/**
* Knowledge entity in Neo4j
*/
export interface Neo4jKnowledge extends Neo4jEntity {
projectId: string;
text: string;
tags?: string[];
// domain is now represented by the BELONGS_TO_DOMAIN relationship
// citations are now represented by the CITES relationship
}
/**
* User entity in Neo4j
*/
export interface Neo4jUser extends Neo4jEntity {
username: string;
displayName: string;
email?: string;
}
/**
* TaskType entity in Neo4j
*/
export interface Neo4jTaskType {
name: string;
description?: string;
}
/**
* Domain entity in Neo4j
*/
export interface Neo4jDomain {
name: string;
description?: string;
}
/**
* Citation entity in Neo4j
*/
export interface Neo4jCitation extends Neo4jEntity {
source: string;
title?: string;
author?: string;
date?: string;
}
/**
* Relationship types used in the Neo4j database
*/
export enum RelationshipTypes {
CONTAINS_TASK = "CONTAINS_TASK",
CONTAINS_KNOWLEDGE = "CONTAINS_KNOWLEDGE",
DEPENDS_ON = "DEPENDS_ON",
ASSIGNED_TO = "ASSIGNED_TO",
CITES = "CITES",
RELATED_TO = "RELATED_TO",
HAS_TYPE = "HAS_TYPE",
BELONGS_TO_DOMAIN = "BELONGS_TO_DOMAIN",
BELONGS_TO_PROJECT = "BELONGS_TO_PROJECT",
}
/**
* Node labels used in the Neo4j database
*/
export enum NodeLabels {
Project = "Project",
Task = "Task",
Knowledge = "Knowledge",
User = "User",
TaskType = "TaskType",
Domain = "Domain",
Citation = "Citation",
}
/**
* Pagination options for querying Neo4j
*/
export interface PaginationOptions {
page?: number;
limit?: number;
}
/**
* Result with pagination metadata
*/
export interface PaginatedResult<T> {
data: T[];
total: number;
page: number;
limit: number;
totalPages: number;
}
/**
* Filter options for Project queries
*/
export interface ProjectFilterOptions extends PaginationOptions {
status?:
| "active"
| "pending"
| "in-progress"
| "completed"
| "archived"
| ("active" | "pending" | "in-progress" | "completed" | "archived")[];
taskType?: string;
searchTerm?: string;
}
/**
* Filter options for Task queries
*/
export interface TaskFilterOptions extends PaginationOptions {
projectId: string;
status?:
| "backlog"
| "todo"
| "in-progress"
| "completed"
| ("backlog" | "todo" | "in-progress" | "completed")[];
priority?:
| "low"
| "medium"
| "high"
| "critical"
| ("low" | "medium" | "high" | "critical")[];
assignedTo?: string; // Filter by user ID (queries relationship)
tags?: string[];
taskType?: string;
sortBy?: "priority" | "createdAt" | "status";
sortDirection?: "asc" | "desc";
}
/**
* Filter options for Knowledge queries
*/
export interface KnowledgeFilterOptions extends PaginationOptions {
projectId: string;
tags?: string[];
domain?: string; // Filter by domain name (queries relationship)
search?: string; // Keep search for text content
}
/**
* Specific types for project dependencies stored within the DEPENDS_ON relationship properties.
*/
export enum ProjectDependencyType {
REQUIRES = "requires",
EXTENDS = "extends",
IMPLEMENTS = "implements",
REFERENCES = "references",
}
/**
* Project dependency relationship type
*/
export interface ProjectDependency {
sourceProjectId: string;
targetProjectId: string;
type: ProjectDependencyType; // Use the enum
description: string;
}
/**
* Search options for unified search
*/
export interface SearchOptions extends PaginationOptions {
property?: string;
value: string;
entityTypes?: ("project" | "task" | "knowledge")[];
caseInsensitive?: boolean;
fuzzy?: boolean;
taskType?: string;
assignedToUserId?: string; // Added for filtering tasks by assignee
}
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_task_delete/deleteTask.ts:
--------------------------------------------------------------------------------
```typescript
import { TaskService } from "../../../services/neo4j/taskService.js";
import { BaseErrorCode, McpError } from "../../../types/errors.js";
import { ResponseFormat, createToolResponse } from "../../../types/mcp.js";
import { logger, requestContextService } from "../../../utils/index.js"; // Import requestContextService
import { ToolContext } from "../../../types/tool.js";
import { AtlasTaskDeleteInput, AtlasTaskDeleteSchema } from "./types.js";
import { formatTaskDeleteResponse } from "./responseFormat.js";
export const atlasDeleteTask = async (input: unknown, context: ToolContext) => {
let validatedInput: AtlasTaskDeleteInput | undefined;
const reqContext =
context.requestContext ??
requestContextService.createRequestContext({ toolName: "atlasDeleteTask" });
try {
// Parse and validate input against schema definition
validatedInput = AtlasTaskDeleteSchema.parse(input);
// Select operation strategy based on request mode
if (validatedInput.mode === "bulk") {
// Process bulk removal operation
const { taskIds } = validatedInput;
logger.info("Initiating batch task removal", {
...reqContext,
count: taskIds.length,
taskIds,
});
const results = {
success: true,
message: `Successfully removed ${taskIds.length} tasks`,
deleted: [] as string[],
errors: [] as {
taskId: string;
error: {
code: string;
message: string;
details?: any;
};
}[],
};
// Process removal operations sequentially to maintain data integrity
for (const taskId of taskIds) {
try {
const deleted = await TaskService.deleteTask(taskId);
if (deleted) {
results.deleted.push(taskId);
} else {
// Task not found
results.success = false;
results.errors.push({
taskId,
error: {
code: BaseErrorCode.NOT_FOUND,
message: `Task with ID ${taskId} not found`,
},
});
}
} catch (error) {
results.success = false;
results.errors.push({
taskId,
error: {
code:
error instanceof McpError
? error.code
: BaseErrorCode.INTERNAL_ERROR,
message: error instanceof Error ? error.message : "Unknown error",
details: error instanceof McpError ? error.details : undefined,
},
});
}
}
if (results.errors.length > 0) {
results.message = `Removed ${results.deleted.length} of ${taskIds.length} tasks with ${results.errors.length} errors`;
}
logger.info("Batch task removal operation completed", {
...reqContext,
successCount: results.deleted.length,
errorCount: results.errors.length,
});
// Conditionally format response
if (validatedInput.responseFormat === ResponseFormat.JSON) {
return createToolResponse(JSON.stringify(results, null, 2));
} else {
return formatTaskDeleteResponse(results);
}
} else {
// Process single entity removal
const { id } = validatedInput;
logger.info("Removing task entity", {
...reqContext,
taskId: id,
});
const deleted = await TaskService.deleteTask(id);
if (!deleted) {
logger.warning("Target task not found for removal operation", {
...reqContext,
taskId: id,
});
throw new McpError(
BaseErrorCode.NOT_FOUND,
`Task with identifier ${id} not found`,
{ taskId: id },
);
}
logger.info("Task successfully removed", {
...reqContext,
taskId: id,
});
const result = {
id,
success: true,
message: `Task with ID ${id} removed successfully`,
};
// Conditionally format response
if (validatedInput.responseFormat === ResponseFormat.JSON) {
return createToolResponse(JSON.stringify(result, null, 2));
} else {
return formatTaskDeleteResponse(result);
}
}
} catch (error) {
// Handle specific error cases
if (error instanceof McpError) {
throw error;
}
logger.error("Task removal operation failed", error as Error, {
...reqContext,
inputReceived: validatedInput ?? input,
});
// Translate unknown errors to structured McpError format
throw new McpError(
BaseErrorCode.INTERNAL_ERROR,
`Failed to remove task(s): ${error instanceof Error ? error.message : "Unknown error"}`,
);
}
};
```
--------------------------------------------------------------------------------
/src/mcp/tools/atlas_task_update/updateTask.ts:
--------------------------------------------------------------------------------
```typescript
import { TaskService } from "../../../services/neo4j/taskService.js";
import { BaseErrorCode, McpError } from "../../../types/errors.js";
import { ResponseFormat, createToolResponse } from "../../../types/mcp.js";
import { logger, requestContextService } from "../../../utils/index.js"; // Import requestContextService
import { ToolContext } from "../../../types/tool.js";
import { AtlasTaskUpdateInput, AtlasTaskUpdateSchema } from "./types.js";
import { formatTaskUpdateResponse } from "./responseFormat.js";
export const atlasUpdateTask = async (input: unknown, context: ToolContext) => {
let validatedInput: AtlasTaskUpdateInput | undefined;
const reqContext =
context.requestContext ??
requestContextService.createRequestContext({ toolName: "atlasUpdateTask" });
try {
// Parse and validate the input against schema
validatedInput = AtlasTaskUpdateSchema.parse(input);
// Process according to operation mode (single or bulk)
if (validatedInput.mode === "bulk") {
// Execute bulk update operation
logger.info("Applying updates to multiple tasks", {
...reqContext,
count: validatedInput.tasks.length,
});
const results = {
success: true,
message: `Successfully updated ${validatedInput.tasks.length} tasks`,
updated: [] as any[],
errors: [] as any[],
};
// Process each task update sequentially to maintain data consistency
for (let i = 0; i < validatedInput.tasks.length; i++) {
const taskUpdate = validatedInput.tasks[i];
try {
// First check if task exists
const taskExists = await TaskService.getTaskById(taskUpdate.id);
if (!taskExists) {
throw new McpError(
BaseErrorCode.NOT_FOUND,
`Task with ID ${taskUpdate.id} not found`,
);
}
// Update the task
const updatedTask = await TaskService.updateTask(
taskUpdate.id,
taskUpdate.updates,
);
results.updated.push(updatedTask);
} catch (error) {
results.success = false;
results.errors.push({
index: i,
task: taskUpdate,
error: {
code:
error instanceof McpError
? error.code
: BaseErrorCode.INTERNAL_ERROR,
message: error instanceof Error ? error.message : "Unknown error",
details: error instanceof McpError ? error.details : undefined,
},
});
}
}
if (results.errors.length > 0) {
results.message = `Updated ${results.updated.length} of ${validatedInput.tasks.length} tasks with ${results.errors.length} errors`;
}
logger.info("Bulk task modification completed", {
...reqContext,
successCount: results.updated.length,
errorCount: results.errors.length,
taskIds: results.updated.map((t) => t.id),
});
// Conditionally format response
if (validatedInput.responseFormat === ResponseFormat.JSON) {
return createToolResponse(JSON.stringify(results, null, 2));
} else {
return formatTaskUpdateResponse(results);
}
} else {
// Process single task modification
const { mode, id, updates } = validatedInput;
logger.info("Modifying task attributes", {
...reqContext,
id,
fields: Object.keys(updates),
});
// First check if task exists
const taskExists = await TaskService.getTaskById(id);
if (!taskExists) {
throw new McpError(
BaseErrorCode.NOT_FOUND,
`Task with ID ${id} not found`,
);
}
// Update the task
const updatedTask = await TaskService.updateTask(id, updates);
logger.info("Task modifications applied successfully", {
...reqContext,
taskId: id,
});
// Conditionally format response
if (validatedInput.responseFormat === ResponseFormat.JSON) {
return createToolResponse(JSON.stringify(updatedTask, null, 2));
} else {
return formatTaskUpdateResponse(updatedTask);
}
}
} catch (error) {
// Handle specific error cases
if (error instanceof McpError) {
throw error;
}
logger.error("Failed to modify task(s)", error as Error, {
...reqContext,
inputReceived: validatedInput ?? input,
});
// Handle not found error specifically
if (error instanceof Error && error.message.includes("not found")) {
throw new McpError(
BaseErrorCode.NOT_FOUND,
`Task not found: ${error.message}`,
);
}
// Convert generic errors to properly formatted McpError
throw new McpError(
BaseErrorCode.INTERNAL_ERROR,
`Failed to modify task(s): ${error instanceof Error ? error.message : "Unknown error"}`,
);
}
};
```