Architecture: MCP Framework & Yandex Tracker Server
December 9, 2025 · View on GitHub
Monorepo Architecture Overview
🎯 Monorepo Principles
1. Package Independence
Каждый пакет может быть опубликован и использован независимо.
2. Clear Dependency Graph
Строгая иерархия зависимостей без циклов.
3. Shared Infrastructure
Общие компоненты (infrastructure, core) переиспользуются.
4. Topological Build Order
Сборка автоматически учитывает зависимости пакетов.
📦 Monorepo Structure
packages/
├── framework/
│ ├── infrastructure/ → @fractalizer/mcp-infrastructure
│ │ ├── http/, cache/, async/, logging/
│ │ └── 0 dependencies
│ ├── cli/ → @fractalizer/mcp-cli
│ │ └── depends on: infrastructure
│ ├── core/ → @fractalizer/mcp-core
│ │ ├── tools/base/, utils/, tool-registry
│ │ └── depends on: infrastructure
│ └── search/ → @fractalizer/mcp-search
│ ├── engine/, strategies/, tools/
│ └── depends on: core
└── servers/
└── yandex-tracker/ → mcp-server-yandex-tracker
├── api_operations/, entities/, tools/, composition-root/
└── depends on: infrastructure, cli, core, search
🔗 Dependency Graph
┌─────────────────┐
│ infrastructure │ ← Base layer (HTTP, logging, cache, async)
└────────┬────────┘
│
↓
┌─────────────────┐
│ core │ ← Framework core (BaseTool, registry, utilities)
└────────┬────────┘
│
↓
┌─────────────────┐
│ search │ ← Tool discovery (search engine, strategies)
└────────┬────────┘
│
↓
┌─────────────────┐
│ yandex-tracker │ ← Application (Yandex.Tracker integration)
└─────────────────┘
Rules:
- ❌ No reverse dependencies (core → infrastructure)
- ❌ No imports from yandex-tracker to framework packages
- ✅ Dependencies flow top-down only
Validation:
npm run depcruise # Validates dependency graph
📦 Package Details
@fractalizer/mcp-infrastructure
Purpose: Reusable infrastructure layer (domain-agnostic)
Components:
- HTTP Layer: HttpClient (Axios wrapper), RetryHandler, ErrorMapper
- Caching: CacheManager interface, NoOpCache
- Async: ParallelExecutor (batch throttling)
- Logging: Pino with rotating-file-stream
- Config: Environment variable loading and validation
Key Principle: Infrastructure does NOT know about domain (Yandex.Tracker, MCP)
Details: packages/framework/infrastructure/README.md
@fractalizer/mcp-core
Purpose: Core framework for building MCP tools
Components:
- Base Classes: BaseTool
, BaseDefinition - Tool Registry: ToolRegistry (lazy initialization)
- Utilities: ResponseFieldFilter, BatchResultProcessor, ResultLogger
- Schemas: Common Zod schemas (fields, expand, issue-key)
Key Principle: Generic BaseTool<TFacade> — facade-agnostic design
Details: packages/framework/core/README.md
@fractalizer/mcp-search
Purpose: Advanced tool discovery with compile-time indexing
Components:
- Engine: ToolSearchEngine (LRU cache)
- Strategies: Name, Description, Category, Fuzzy, WeightedCombined
- Tools: SearchToolsTool (MCP tool for Claude)
- Index: generated-index.ts (auto-generated at build)
Key Principle: Compile-time indexing (zero runtime overhead)
Details: packages/framework/search/README.md
mcp-server-yandex-tracker
Purpose: Complete MCP server for Yandex.Tracker API v3
Components:
- API Operations: Batch operations for issues, users, comments
- Entities: Domain types (Issue, User, Queue, etc.)
- DTO: Data Transfer Objects (create, update requests)
- MCP Tools: API tools + helpers
- DI: InversifyJS composition root
Key Principle: Built on framework packages (infrastructure, core, search)
Details: packages/servers/yandex-tracker/README.md, packages/servers/yandex-tracker/CLAUDE.md
🏗️ Architectural Principles (Shared)
1. Feature-by-Folder
Группируем файлы по функциональности, а не по типу.
✅ Правильно:
api/http/retry/
├── retry-handler.ts
├── retry-strategy.interface.ts
└── exponential-backoff.strategy.ts
❌ Неправильно:
strategies/
└── exponential-backoff.ts
handlers/
└── retry-handler.ts
2. Single Responsibility Principle (SRP)
Каждый класс/файл отвечает ТОЛЬКО за одну задачу.
3. Dependency Injection
Все зависимости через конструктор (InversifyJS в yandex-tracker).
4. Interface Segregation
Минимальные, специфичные интерфейсы.
5. Open/Closed Principle
Открыто для расширения, закрыто для модификации.
📥 Module System: Node.js Subpath Imports
Решение (2025): Проект использует Node.js Subpath Imports вместо TypeScript path aliases.
Почему Subpath Imports?
- Нативная поддержка Node.js (12.19.0+) - не требует tsc-alias для внутренних импортов
- Рекомендация Turborepo team для monorepo проектов
- Одна точка конфигурации - только package.json (не нужны tsconfig paths)
- Полная поддержка TypeScript 5.4+ - автокомплит, LSP, навигация
- Избежание конфликтов - префикс
#не конфликтует с npm scoped packages
Конфигурация
package.json (yandex-tracker):
{
"imports": {
"#tracker_api/*": "./src/tracker_api/*",
"#tools/*": "./src/tools/*",
"#composition-root/*": "./src/composition-root/*",
"#cli/*": "./src/cli/*",
"#constants": "./src/constants.ts",
"#common/*": "./src/common/*",
"#integration/*": "./tests/integration/*",
"#helpers/*": "./tests/helpers/*"
}
}
Правила импортов
1. Междупакетные (npm package names):
import { BaseTool } from '@fractalizer/mcp-core';
import { HttpClient } from '@fractalizer/mcp-infrastructure';
2. Внутрипакетные короткие (≤2 уровня - относительные пути):
import { validateInput } from './utils.js';
import { BaseOperation } from '../base-operation.js';
3. Внутрипакетные глубокие (≥3 уровня - subpath imports):
import { MCP_TOOL_PREFIX } from '#constants';
import { YandexTrackerFacade } from '#tracker_api/facade/yandex-tracker.facade.js';
import { createFixture } from '#helpers/queue.fixture.js';
Миграция с TypeScript Path Aliases
Было (TypeScript paths):
{
"compilerOptions": {
"paths": {
"@tracker_api/*": ["./src/tracker_api/*"],
"@tools/*": ["./src/tools/*"]
}
}
}
Стало (Node.js subpath imports):
{
"imports": {
"#tracker_api/*": "./src/tracker_api/*",
"#tools/*": "./src/tools/*"
}
}
Детали миграции: См. .agentic-planning/plan_migrate_to_subpath_imports/
🔄 Data Flow (Yandex Tracker Server)
Request Chain:
1. Claude Desktop (MCP Client)
↓ JSON-RPC via stdio
2. MCP Server (index.ts)
↓ tools/call
3. ToolRegistry
↓ route to tool
4. Concrete Tool (e.g., GetIssuesTool)
↓ validate params (Zod)
5. YandexTrackerFacade
↓ delegate to operation
6. Operation (e.g., GetIssuesOperation)
↓ business logic
7. HttpClient (with retry)
↓ HTTPS request
8. Yandex.Tracker API v3
↓ response
9. IssueWithUnknownFields (preserves unknown fields)
↓ filter fields
10. ResponseFieldFilter
↓ format for Claude
11. Tool returns result
Layer Responsibilities:
- Tools — validation, formatting for Claude
- Facade — high-level API for tools
- Operations — business logic
- Infrastructure — HTTP, retry, cache, logging
Batch Operations Flow
All read and write operations support batch mode for working with multiple issues efficiently.
GET operations (parallel data fetching):
Tool (issueIds[]) → Operation.executeMany()
→ ParallelExecutor (respects maxConcurrentRequests from ServerConfig)
→ N × API calls (parallel, with throttling)
→ BatchResult<string, Data>
→ BatchResultProcessor.process()
→ Unified format: { total, successful, failed, fieldsReturned }
POST/DELETE operations (parallel modification):
Tool (items[]) → Operation.executeMany()
→ ParallelExecutor (throttles to maxConcurrentRequests)
→ N × API calls (each with individual params)
→ BatchResult<string, Response>
→ BatchResultProcessor.process()
→ Unified format: { total, successful, failed }
Key components:
- ParallelExecutor — enforces maxConcurrentRequests from ServerConfig
- BatchResultProcessor — unifies result processing for all batch operations
- Unified Batch Format — consistent response structure across all operations
Two patterns:
- GET batch (shared parameters): Single set of parameters (perPage, expand) applied to all issues
- Schema:
issueIds: IssueKeysSchema(array, min 1)
- Schema:
- POST/DELETE batch (individual parameters): Each issue has its own parameters
- Schema: array of objects
[{ issueId, ...params }]
- Schema: array of objects
Examples: get-comments.tool.ts, add-comment.tool.ts, delete-link.tool.ts
🔄 Schema-to-Definition Generator
Problem: Manual creation of MCP definitions leads to schema-definition mismatch bugs.
Solution: Automatic generation of MCP definition from Zod schema.
Architecture
Zod Schema (*.schema.ts)
↓
generateDefinitionFromSchema()
↓
MCP Definition (runtime)
Implementation
Tool class:
export class GetIssuesTool extends BaseTool<typeof GetIssuesSchema> {
getDefinition(): ToolDefinition {
return generateDefinitionFromSchema(this.metadata, GetIssuesSchema);
}
}
Generator (@fractalizer/mcp-core):
- Uses Zod v4 native
toJSONSchema()API - Converts JSON Schema to MCP Definition format
- Extracts descriptions from
.describe()calls - Validates required vs optional fields
Benefits
- ✅ DRY Principle — single source of truth (schema)
- ✅ No Mismatch — physically impossible to create inconsistency
- ✅ Simpler Tools — no separate
*.definition.tsfiles - ✅ Auto-sync — schema changes automatically reflected in definition
Migration
Old: Separate *.schema.ts + *.definition.ts files (removed)
New: Only *.schema.ts with generateDefinitionFromSchema()
Details: .agentic-planning/plan_prevent_schema_definition_mismatch_bugs/
📦 Entities & DTO: Forward Compatibility
Pattern: Separate types by data flow direction
Incoming (from API): *WithUnknownFields
// packages/servers/yandex-tracker/src/entities/issue.entity.ts
export interface Issue { /* known fields */ }
export type IssueWithUnknownFields = WithUnknownFields<Issue>;
Purpose: Preserve unknown fields added by Yandex.Tracker
Outgoing (to API): Strict DTO
// packages/servers/yandex-tracker/src/dto/issue/update-issue.dto.ts
export interface UpdateIssueDto {
summary?: string;
description?: string;
// NO index signature (type-safe)
}
Purpose: Type-safe requests
Details: packages/servers/yandex-tracker/src/entities/README.md, packages/servers/yandex-tracker/src/dto/README.md
🚀 Batch Operations (Yandex Tracker)
Principle: All collection operations use batch approach
Pattern:
getIssues(keys[])— batch getcreateIssues(requests[])— batch createupdateIssues(items[])— batch update
Why:
- Universality (1 or N items)
- Automatic throttling (ParallelExecutor)
- Simplified architecture (no code duplication)
Implementation:
// ParallelExecutor with 2 independent limits
const executor = new ParallelExecutor(config);
const results = await executor.execute(
keys,
(key) => httpClient.get<Issue>(`/v3/issues/${key}`)
);
// results: BatchResult<string, Issue>
Limits:
- MAX_BATCH_SIZE (business): 200 items per chunk
- MAX_CONCURRENT_REQUESTS (technical): 5 concurrent requests
Result Type: BatchResult<T> (discriminated union: fulfilled | rejected)
Details: packages/framework/infrastructure/README.md
🔧 Dependency Injection (Yandex Tracker)
Approach: InversifyJS v7 with Symbol-based tokens
Structure:
packages/servers/yandex-tracker/src/composition-root/
├── types.ts # Symbol tokens (TYPES.HttpClient, etc.)
├── container.ts # Container configuration
└── definitions/ # Declarative definitions
├── tool-definitions.ts
└── operation-definitions.ts
Benefits:
- Works with interfaces
- Easy testing (rebind)
- Explicit contracts
Details: packages/servers/yandex-tracker/src/composition-root/README.md
🔍 Tool Search System
Architecture:
-
Compile-time Indexing:
npm run build # → runs scripts/generate-tool-index.ts # → generates packages/search/src/generated-index.ts -
Runtime Search:
const engine = new ToolSearchEngine(TOOL_INDEX); const results = engine.search('find issues'); -
5 Search Strategies:
- NameSearchStrategy (exact/partial match)
- DescriptionSearchStrategy (word matching)
- CategorySearchStrategy (category filter)
- FuzzySearchStrategy (Levenshtein distance)
- WeightedCombinedStrategy (combine all)
-
LRU Cache:
- Max 100 entries
- Key:
${query}_${strategy}
Details: packages/framework/search/README.md
🔒 Architecture Validation (dependency-cruiser)
Rules:
-
Layered Architecture
yandex-trackerне импортирует в framework пакетыinfrastructureне импортирует domain слои
-
Package Boundaries
- Импорты между пакетами только через npm package names
- Нет относительных путов между пакетами
-
MCP Isolation (yandex-tracker)
- Tools используют только Facade, не Operations напрямую
- Разрешены импорты entities/dto для типов
-
No Circular Dependencies
- Запрещены циклические зависимости
Validation:
npm run depcruise # Check all rules
npm run depcruise:graph # Generate dependency graph
Config: .dependency-cruiser.cjs
Integration: Rules checked in npm run validate
🧪 Testing Strategy
Unit Tests
Structure: packages/*/tests/ mirrors packages/*/src/
Framework: Vitest (ESM + TypeScript)
Coverage: ≥80% for all packages
Patterns:
- AAA (Arrange, Act, Assert)
- Mocks for external dependencies
- Test both happy path and error cases
Commands:
npm run test # All packages
npm run test:coverage # With coverage
npm run test --workspace=@fractalizer/mcp-core # Single package
Details: packages/servers/yandex-tracker/tests/README.md
📋 Adding New Functionality
Adding Framework Package
- Create
packages/new-package/ - Add
package.jsonwith correct dependencies - Add
tsconfig.jsonwith project references - Update root
package.jsonworkspaces - Update root
tsconfig.jsonreferences - Update
.dependency-cruiser.cjsrules - Create README.md
npm install && npm run build
Adding MCP Tool (in yandex-tracker)
-
Create structure:
packages/servers/yandex-tracker/src/mcp/tools/{api|helpers}/{feature}/{action}/ ├── {name}.schema.ts ├── {name}.definition.ts ├── {name}.tool.ts └── index.ts -
Add to registry:
// packages/servers/yandex-tracker/src/composition-root/definitions/tool-definitions.ts export const TOOL_CLASSES = [ // ... NewTool, ] as const; -
Tests +
npm run validate
Details: packages/servers/yandex-tracker/src/mcp/README.md
Adding API Operation (in yandex-tracker)
- Create
packages/servers/yandex-tracker/src/api_operations/{feature}/{action}/{name}.operation.ts - Extend
BaseOperation - Add facade method
- Register in
packages/servers/yandex-tracker/src/composition-root/definitions/operation-definitions.ts - Tests +
npm run validate
Details: packages/servers/yandex-tracker/src/api_operations/README.md
🚀 Build & Release Process
Build Order (Topological)
npm run build
# Builds in order:
# 1. infrastructure
# 2. core (depends on infrastructure)
# 3. search (depends on core)
# 4. yandex-tracker (depends on all)
Version Management
Tool: Changesets (https://github.com/changesets/changesets)
Workflow:
npx changeset add— describe changesnpx changeset version— bump versionsgit commit && git push- GitHub Actions publishes to npm
Manual publish:
npm run publish:all
🔍 Code Quality Tools
Linting & Formatting
- ESLint — code quality (max-params, complexity)
- Prettier — code formatting (via pre-commit hook)
- TypeScript — type checking (strict mode)
Security
- Socket.dev — supply-chain analysis
- Gitleaks — secret scanning (pre-commit hook)
Dead Code Detection
- Knip — unused files/exports/dependencies
Lockfile Validation
- Ensures package-lock.json is in sync
Run all:
npm run validate
📚 Documentation Structure
Monorepo Root
- README.md — Overview, quick start
- CLAUDE.md — Monorepo rules for AI agents
- ARCHITECTURE.md (this file) — Architecture overview
Framework Packages
- packages/framework/infrastructure/README.md — Infrastructure API
- packages/framework/core/README.md — Core API
- packages/framework/search/README.md — Search system
Yandex Tracker
- packages/servers/yandex-tracker/README.md — User guide
- packages/servers/yandex-tracker/CLAUDE.md — Developer rules
- Module READMEs:
🎯 Design Patterns Used
Framework Level
- Strategy Pattern — Search strategies, retry strategies
- Null Object — NoOpCache
- Factory Pattern — Tool creation in registry
- Template Method — BaseTool, BaseDefinition
Application Level (Yandex Tracker)
- Facade Pattern — YandexTrackerFacade
- Registry Pattern — ToolRegistry
- Dependency Injection — InversifyJS container
- Repository Pattern — Operations as repositories
📊 Performance Considerations
Compile-time Optimization
- Tool index generated at build (not runtime)
- TypeScript compilation with project references
- Incremental builds
Runtime Optimization
- Lazy tool initialization (ToolRegistry)
- LRU cache (tool search)
- Batch operations (parallel execution)
- Field filtering (80-90% response size reduction)
Bundle Size
- Tree-shaking friendly (ESM modules)
- Separate packages (install only what you need)
- No dynamic requires
🔗 External Resources
- MCP Specification: https://github.com/anthropics/mcp
- Yandex.Tracker API: https://cloud.yandex.ru/docs/tracker/about-api
- InversifyJS: https://inversify.io/
- Zod: https://zod.dev/
- Vitest: https://vitest.dev/
- dependency-cruiser: https://github.com/sverweij/dependency-cruiser