Backend (Spring Boot 3.2 / Java 21 / PostgreSQL): - JWT auth with BCrypt password hashing - User profile + Mifflin-St Jeor BMR calculator - Food search + barcode via OpenFoodFacts API with local cache - Meal CRUD with user data isolation and ownership checks - AI photo analysis (OpenAI Vision) with confidence intervals - AI correction feedback loop for personalisation - Flyway DB migrations + RFC-7807 error responses Mobile (React Native / TypeScript): - Full navigation stack (Auth → Tabs → Home stack) - Design tokens (WCAG 2.2 AA colours, 8px grid, 48px touch targets) - 10 screens: Login, Register, Home, Search, Camera, AI Result, Edit Meal, Daily Details, History, Profile - Confidence-aware calorie display (kcal ± range) - Repeat last meal shortcut + macro tracking Docs: - docs/PLAN-AND-REQUIREMENTS.md - docs/traceability.csv (35 requirements, all Implemented)
14 KiB
All MCP tools use stdio transport. Every tool validates input with Zod before processing.
MCP Standards - Layer 2
Module: MCP Standards Component: Layer 2 (Model Context Protocol Server) Load: When working on virsaitis-development/virsaitis-mcp/ Version: 3.0.0 Updated: 2026-04-20
🎯 Purpose
Defines TypeScript standards, MCP SDK usage, and development workflow for Virsaitis MCP Server (Layer 2 governance enforcement).
🤖 Machine Policy
[TECHNOLOGY_STACK]
LANGUAGE=TypeScript 5.0+
RUNTIME=Node.js 18+
FRAMEWORK=@modelcontextprotocol/sdk
BUILD=tsc + esbuild
TEST=vitest
LINT=eslint + prettier
[CODE_STANDARDS]
INDENTATION=2_spaces
LINE_LENGTH=100_chars
QUOTES=single
SEMICOLONS=required
TRAILING_COMMAS=required_multiline
[QUALITY_GATES]
BUILD=must_succeed
TESTS=must_pass
LINT=zero_errors
TYPE_CHECK=strict_mode
COVERAGE=70_percent_min
📐 TypeScript Standards (TIER-1)
Indentation & Formatting
REQUIRED:
- Indentation: 2 spaces (not 4, not tabs)
- Line length: 100 characters maximum
- Quotes: Single quotes
'string'for strings - Semicolons: Required at end of statements
- Trailing commas: Required for multiline arrays/objects
✅ GOOD:
const config = {
server: 'virsaitis-mcp',
port: 3000,
enabled: true,
};
❌ BAD:
const config = {
server: "virsaitis-mcp",
port: 3000,
enabled: true
} // Missing trailing comma, 4 spaces, double quotes
File Organization
STANDARD ORDER:
// 1. External imports (Node.js, npm packages)
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import * as fs from 'fs';
// 2. Internal imports (project files)
import { GovernanceValidator } from './governance/validator.js';
import { PolicyEngine } from './policy/engine.js';
// 3. Type definitions
interface ValidationResult {
allowed: boolean;
reason?: string;
}
// 4. Constants
const PROTECTED_PATTERNS = [
'.github/copilot-instructions.md',
'requirements/**',
];
// 5. Class/function implementations
export class VirsaitisMCPServer {
// Implementation
}
Naming Conventions (TIER-1)
| Element | Convention | Example |
|---|---|---|
| Classes | PascalCase | GovernancePolicyValidator |
| Interfaces | PascalCase | PolicyResult or IPolicyResult |
| Types | PascalCase | OperationType |
| Functions | camelCase | validateFileOperation |
| Methods | camelCase | checkPermissions |
| Variables | camelCase | isValid, fileName |
| Constants | UPPER_SNAKE_CASE | MAX_RETRIES, PROTECTED_PATTERNS |
| Private members | Leading underscore | _config, _cache |
| Enums | PascalCase | TierLevel |
| Enum values | PascalCase | TierLevel.Critical |
🔧 MCP Server Architecture
Server Structure
virsaitis-development/virsaitis-mcp/
├── src/
│ ├── index.ts (server entry point)
│ ├── server.ts (MCP server class)
│ ├── governance/
│ │ ├── types.ts (TierLevel, GovernanceRule, ValidationResult)
│ │ ├── patterns.ts (glob pattern matching)
│ │ ├── cache.ts (in-memory governance cache)
│ │ ├── loader.ts (parse core-policies.md + agent files)
│ │ └── validator.ts (GovernanceValidator - TIER validation)
│ ├── config.ts (server configuration - REQ-MCP-010)
│ ├── tools/
│ │ ├── scan-secrets.ts (mcp_virsaitis_scan_secrets)
│ │ ├── validate-path.ts (mcp_virsaitis_validate_path)
│ │ ├── validate-command.ts (mcp_virsaitis_validate_command)
│ │ ├── audit-logger.ts (mcp_virsaitis_read_audit_log)
│ │ └── iteration-complete.ts (mcp_virsaitis_iteration_complete)
├── tests/
│ ├── unit/
│ ├── integration/
│ └── fixtures/
├── build/ (compiled output)
├── package.json
├── tsconfig.json
├── vitest.config.ts
└── README.md
MCP Tools Implementation
TOOL PATTERN:
// Tool definition
server.setRequestHandler(ToolsListRequestSchema, async () => {
return {
tools: [
{
name: 'mcp_virsaitis_validate_operation',
description: 'Validates if an operation is allowed by governance policy',
inputSchema: {
type: 'object',
properties: {
operation: {
type: 'string',
description: 'Operation type: read, write, delete, execute',
},
filePath: {
type: 'string',
description: 'Absolute file path',
},
},
required: ['operation', 'filePath'],
},
},
],
};
});
// Tool execution
server.setRequestHandler(ToolCallRequestSchema, async (request) => {
if (request.params.name === 'mcp_virsaitis_validate_operation') {
const { operation, filePath } = request.params.arguments;
// Validation logic
const result = await governanceValidator.validate(operation, filePath);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
}
});
⚡ CHECKPOINT — Is this MCP tool using Zod input validation? Every tool parameter must have a schema.
✅ Type Safety (TIER-1)
TypeScript Configuration
tsconfig.json REQUIREMENTS:
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": false,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./build",
"rootDir": "./src"
}
}
STRICT MODE REQUIRED:
strict: true(enables all strict checks)noImplicitAny: true(no implicit any types)strictNullChecks: true(null/undefined handling)strictFunctionTypes: true(function type checking)strictPropertyInitialization: true(class property init)
Explicit Type Annotations
REQUIRED FOR:
- Public function return types
- Public method return types
- Exported interfaces/types
- Complex function parameters
✅ GOOD:
export function validateTier(tier: string): boolean {
return ['TIER-0', 'TIER-1', 'TIER-2', 'TIER-3'].includes(tier);
}
export interface PolicyResult {
allowed: boolean;
tier: string;
reason?: string;
consequences?: Consequence[];
}
❌ BAD:
export function validateTier(tier) { // Missing parameter type
return ['TIER-0', 'TIER-1', 'TIER-2', 'TIER-3'].includes(tier);
} // Missing return type
export interface PolicyResult {
allowed; // Missing type
tier; // Missing type
}
🧪 Testing Standards (TIER-1)
Test Framework
USING: Vitest (fast, TypeScript-native)
vitest.config.ts:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
lines: 70,
functions: 70,
branches: 70,
statements: 70,
},
},
});
Test Structure
PATTERN:
import { describe, it, expect, beforeEach } from 'vitest';
import { GovernanceValidator } from '../src/governance/validator';
describe('GovernanceValidator', () => {
let validator: GovernanceValidator;
beforeEach(() => {
validator = new GovernanceValidator();
});
describe('validateFileOperation', () => {
it('should block protected file modification', () => {
// Given
const operation = 'write';
const filePath = '.github/copilot-instructions.md';
// When
const result = validator.validateFileOperation(operation, filePath);
// Then
expect(result.allowed).toBe(false);
expect(result.tier).toBe('TIER-0');
expect(result.reason).toContain('protected file');
});
it('should allow non-protected file modification', () => {
// Given
const operation = 'write';
const filePath = 'src/my-file.ts';
// When
const result = validator.validateFileOperation(operation, filePath);
// Then
expect(result.allowed).toBe(true);
});
});
});
Test Coverage Requirements
MINIMUM COVERAGE:
- Overall: 70%
- Security-critical code: 100%
- Governance validation: 100%
- Consequence evaluation: 100%
- Tool implementations: 90%
- Utilities: 70%
MEASURE:
npm run test:coverage
🔒 Security Standards
Input Validation
ALWAYS VALIDATE:
function validateFilePath(filePath: string): string {
// Check for null/undefined
if (!filePath) {
throw new Error('File path is required');
}
// Check for path traversal
if (filePath.includes('..')) {
throw new Error('Path traversal detected');
}
// Normalize path
const normalized = path.normalize(filePath);
// Ensure absolute path
if (!path.isAbsolute(normalized)) {
throw new Error('Absolute path required');
}
return normalized;
}
```\n\n> \u26a1 CHECKPOINT \u2014 MCP uses stdio transport only. If you see HTTP fetch or REST endpoints, that code is wrong.\n\n### Error Handling", "oldString": "```\n\n### Error Handling
- Internal file paths in error messages
- Sensitive configuration
- Stack traces to external systems
- Credentials or secrets
✅ **GOOD**:
```typescript
try {
await fs.promises.readFile(filePath);
} catch (error) {
// Log full error internally
logger.error('File read failed', { filePath, error });
// Return sanitized error to user
return {
success: false,
message: 'Unable to read file',
};
}
❌ BAD:
try {
await fs.promises.readFile(filePath);
} catch (error) {
// Exposes internal path
return {
success: false,
message: `Failed to read ${filePath}: ${error.message}`,
};
}
🔄 Build & Development Workflow
Development Commands
# Install dependencies
npm install
# Start development with file watching
npm run dev
# Build TypeScript
npm run build
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Run linter
npm run lint
# Fix linting issues
npm run lint:fix
# TypeScript type checking
npm run type-check
# Format code
npm run format
Before Commit Checklist (TIER-1)
ALL MUST PASS:
npm run build # ✅ Must succeed
npm test # ✅ Must pass (all tests)
npm run lint # ✅ Zero errors
npm run type-check # ✅ No type errors
npm run test:coverage # ✅ Coverage ≥70%
IF ANY FAIL: Fix before committing
📦 MCP Server Packaging
Build Output
COMPILED TO: build/ directory
INCLUDES:
build/index.js(entry point)build/**/*.js(compiled TypeScript)build/**/*.d.ts(type definitions)build/**/*.js.map(source maps)
NPM Package
package.json ESSENTIALS:
{
"name": "@virsaitis/mcp-server",
"version": "2.0.0",
"type": "module",
"main": "./build/index.js",
"types": "./build/index.d.ts",
"bin": {
"virsaitis-mcp": "./build/index.js"
},
"engines": {
---
## Key Rules From This Module
- stdio transport only. No HTTP REST endpoints for MCP communication.
- Every tool input validated with Zod schemas before processing.
- TypeScript strict mode. No `any` types without documented justification.
- All dependencies must be in DEPENDENCY-REGISTER.md before use.
- Definitions: `.github/virsaitis-definition-library.md`
Return to hub: `.github/copilot-instructions.md`
"node": ">=18.0.0"
},
"scripts": {
"build": "tsc && esbuild",
"test": "vitest run",
"test:coverage": "vitest run --coverage",
"lint": "eslint src/",
"type-check": "tsc --noEmit"
}
}
⚡ CHECKPOINT — All dependencies approved? Check virsaitis-mcp/DEPENDENCY-REGISTER.md before adding packages.
🔗 Integration with Agent & Extension
Agent → MCP Communication
Agent calls MCP tools:
[Agent.md instruction]
Before editing protected file, call mcp_virsaitis_validate_operation tool.
Tool returns whether operation allowed.
If not allowed, respond with TIER-0 VIOLATION PREVENTED.
MCP response format:
interface ValidationResponse {
allowed: boolean;
tier: 'TIER-0' | 'TIER-1' | 'TIER-2' | 'TIER-3';
reason?: string;
consequences?: {
operation: string;
userImpact: string;
technicalImpact: string;
businessImpact: string;
remediation: string;
};
}
MCP ← Extension Communication
Extension queries MCP:
- User tries to edit file
- Extension calls mcp_virsaitis_validate_operation
- MCP validates against governance
- Extension shows 🛡️ shield if protected
- Extension blocks action if TIER-0
💡 Best Practices
Code Organization
ONE CONCERN PER FILE:
- Each file handles one specific responsibility
- Validators in
governance/ - Tools in
tools/ - Utilities in
utils/
SMALL FUNCTIONS:
- Keep functions <50 lines
- Single responsibility
- Testable in isolation
AVOID GOD CLASSES:
- Break large classes into smaller components
- Use composition over inheritance
- Inject dependencies
Performance
CACHING:
class GovernanceCache {
private _rulesCache: Map<string, Rule[]> = new Map();
private _cacheExpiry = 5 * 60 * 1000; // 5 minutes
async getRules(category: string): Promise<Rule[]> {
const cached = this._rulesCache.get(category);
if (cached && !this.isExpired(cached)) {
return cached;
}
const rules = await this.loadRules(category);
this._rulesCache.set(category, rules);
return rules;
}
}
📚 Quick Reference
| Aspect | Standard | Command |
|---|---|---|
| Indentation | 2 spaces | ESLint enforces |
| Build | tsc + esbuild |
npm run build |
| Test | Vitest | npm test |
| Coverage | ≥70% | npm run test:coverage |
| Lint | ESLint + Prettier | npm run lint |
| Type Check | TypeScript strict | npm run type-check |
MCP Standards Module v3.0.0 TypeScript governance enforcement server