Skip to main content
Glama
SYSTEM_ARCHITECTURE.mdβ€’16.9 kB
# πŸ—οΈ μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜ - WorkflowMCP Dashboard API ## 🎯 μ•„ν‚€ν…μ²˜ κ°œμš” **WorkflowMCP Dashboard API**λŠ” κΈ°μ‘΄ μ‹œμŠ€ν…œμ„ ν™•μž₯ν•˜μ—¬ λͺ¨λ“  MCP 도ꡬ와 λŒ€μ‹œλ³΄λ“œ κΈ°λŠ₯을 REST API둜 μ œκ³΅ν•˜λŠ” **자기 μ„€λͺ…적(Self-Descriptive) API μ‹œμŠ€ν…œ**μž…λ‹ˆλ‹€. ### 핡심 섀계 원칙 1. **κΈ°μ‘΄ μ‹œμŠ€ν…œ λ¬΄μˆ˜μ •**: κΈ°μ‘΄ μ½”λ“œ μˆ˜μ • 없이 μΆ”κ°€ κ΅¬ν˜„λ§Œ 2. **자기 μ„€λͺ…적**: API μžμ²΄μ—μ„œ μ‚¬μš©λ²•κ³Ό ꡬ쑰λ₯Ό 제곡 3. **μ„Έμ…˜ μΉœν™”μ **: μƒˆλ‘œμš΄ μ„Έμ…˜μ—μ„œλ„ μ‰½κ²Œ API 탐색 κ°€λŠ₯ 4. **단계적 발견**: Help β†’ Discovery β†’ Usage 흐름 ## πŸ›οΈ 전체 μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜ ```mermaid graph TB subgraph "Client Layer" CC[Claude Code Session] WB[Web Dashboard] TP[Third-party Tools] MA[Mobile Apps] end subgraph "API Gateway Layer" AG[API Gateway/Router] HA[Help API] DA[Discovery API] end subgraph "API Service Layer" PS[PRD Service] TS[Task Service] DS[Document Service] AS[Analytics Service] SS[SSE Service] end subgraph "Core Layer (Existing)" MCP[MCP Server] PM[PRDManager] TM[TaskManager] DM[DocumentManager] end subgraph "Data Layer (Existing)" SQL[(SQLite DB)] JSON[JSON Backup] FTS[FTS Index] end CC --> AG WB --> AG TP --> AG MA --> AG AG --> HA AG --> DA AG --> PS AG --> TS AG --> DS AG --> AS AG --> SS PS --> PM TS --> TM DS --> DM AS --> PM AS --> TM PM --> SQL TM --> SQL DM --> SQL DM --> FTS SQL --> JSON ``` ## πŸ” 자기 μ„€λͺ…적 API 섀계 ### 1. API Discovery 계측 ꡬ쑰 #### Level 0: Root API Discovery ``` GET /api { "name": "WorkflowMCP Dashboard API", "version": "1.0", "description": "Complete API for WorkflowMCP system", "help": "/api/help", "discovery": "/api/discovery", "categories": { "prds": "/api/prds", "tasks": "/api/tasks", "documents": "/api/documents", "analytics": "/api/analytics", "system": "/api/system" }, "guides": { "getting_started": "/api/help/getting-started", "authentication": "/api/help/auth", "examples": "/api/help/examples" } } ``` #### Level 1: Category Discovery ``` GET /api/prds { "category": "PRD Management", "description": "Product Requirements Document management", "help": "/api/help/prds", "endpoints": { "list": "GET /api/prds", "create": "POST /api/prds", "get": "GET /api/prds/{id}", "update": "PUT /api/prds/{id}", "delete": "DELETE /api/prds/{id}" }, "schemas": "/api/schemas/prd", "examples": "/api/examples/prds" } ``` ### 2. Help API μ‹œμŠ€ν…œ #### 관리 메뉴별 Help API ꡬ쑰 ``` /api/help/ β”œβ”€β”€ /getting-started # 전체 API μ‚¬μš©λ²• β”œβ”€β”€ /prds/ # PRD 관리 도움말 β”‚ β”œβ”€β”€ /overview # PRD κΈ°λŠ₯ κ°œμš” β”‚ β”œβ”€β”€ /crud-operations # κΈ°λ³Έ CRUD β”‚ β”œβ”€β”€ /linking # λ¬Έμ„œ μ—°κ²° β”‚ └── /examples # μ‚¬μš© 예제 β”œβ”€β”€ /tasks/ # Task 관리 도움말 β”‚ β”œβ”€β”€ /overview β”‚ β”œβ”€β”€ /dependencies # μ˜μ‘΄μ„± 관리 β”‚ β”œβ”€β”€ /status-workflow # μƒνƒœ λ³€κ²½ β”‚ └── /examples β”œβ”€β”€ /documents/ # Document 관리 도움말 β”‚ β”œβ”€β”€ /overview β”‚ β”œβ”€β”€ /search # 검색 κΈ°λŠ₯ β”‚ β”œβ”€β”€ /types # λ¬Έμ„œ νƒ€μž… β”‚ └── /examples β”œβ”€β”€ /analytics/ # Analytics 도움말 β”‚ β”œβ”€β”€ /overview β”‚ β”œβ”€β”€ /dashboards # λŒ€μ‹œλ³΄λ“œ 데이터 β”‚ └── /examples └── /system/ # μ‹œμŠ€ν…œ 관리 도움말 β”œβ”€β”€ /health # ν—¬μŠ€ 체크 β”œβ”€β”€ /batch # 배치 μž‘μ—… └── /events # μ‹€μ‹œκ°„ 이벀트 ``` ### 3. μ„Έμ…˜λ³„ API μ‚¬μš© νŒ¨ν„΄ #### μ‹ κ·œ μ„Έμ…˜ μ‹œμž‘ μ‹œ ꢌμž₯ 흐름 1. **API 탐색**: `GET /api` β†’ 전체 ꡬ쑰 νŒŒμ•… 2. **도움말 확인**: `GET /api/help/getting-started` β†’ μ‚¬μš©λ²• ν•™μŠ΅ 3. **μΉ΄ν…Œκ³ λ¦¬ 선택**: `GET /api/prds` β†’ νŠΉμ • μ˜μ—­ 탐색 4. **예제 확인**: `GET /api/help/prds/examples` β†’ μ‹€μ œ μ‚¬μš©λ²• 5. **μŠ€ν‚€λ§ˆ 확인**: `GET /api/schemas/prd` β†’ 데이터 ꡬ쑰 6. **μ‹€μ œ μ‚¬μš©**: `POST /api/prds` β†’ API 호좜 ## πŸ”§ API μ„œλΉ„μŠ€ λ ˆμ΄μ–΄ 섀계 ### 1. API Router ꡬ쑰 ```javascript // api-router.js (μƒˆλ‘œ 생성) const express = require('express'); const router = express.Router(); // Discovery & Help Routes router.get('/', apiDiscoveryController.getRoot); router.get('/help/*', helpController.getHelp); router.get('/discovery/*', discoveryController.getDiscovery); router.get('/schemas/*', schemaController.getSchema); router.get('/examples/*', exampleController.getExample); // Core API Routes router.use('/prds', prdApiRouter); router.use('/tasks', taskApiRouter); router.use('/documents', documentApiRouter); router.use('/analytics', analyticsApiRouter); router.use('/system', systemApiRouter); module.exports = router; ``` ### 2. Service Layer νŒ¨ν„΄ ```javascript // services/PrdApiService.js (μƒˆλ‘œ 생성) class PrdApiService { constructor(prdManager) { this.prdManager = prdManager; // κΈ°μ‘΄ PRDManager μž¬μ‚¬μš© } async listPrds(filters = {}) { // κΈ°μ‘΄ MCP 도ꡬ 둜직 μž¬μ‚¬μš© const result = await this.prdManager.listPRDs( filters.status, filters.project_id, filters.sort_by ); // API ν˜•μ‹μœΌλ‘œ λ³€ν™˜ return this.formatApiResponse(result); } formatApiResponse(mcpResult) { return { success: true, data: mcpResult.prds || mcpResult, message: mcpResult.message || 'Success', timestamp: new Date().toISOString(), links: this.generateHateoasLinks() }; } generateHateoasLinks() { return { self: '/api/prds', help: '/api/help/prds', schema: '/api/schemas/prd', examples: '/api/help/prds/examples' }; } } ``` ### 3. HATEOAS 링크 μžλ™ 생성 λͺ¨λ“  API 응닡에 κ΄€λ ¨ 링크 포함: ```json { "success": true, "data": { /* μ‹€μ œ 데이터 */ }, "links": { "self": "/api/prds/123", "update": "PUT /api/prds/123", "delete": "DELETE /api/prds/123", "documents": "/api/prds/123/documents", "help": "/api/help/prds", "examples": "/api/help/prds/examples" } } ``` ## πŸ“Š κΈ°μ‘΄ μ‹œμŠ€ν…œ 톡합 μ „λž΅ ### 1. κΈ°μ‘΄ μ»΄ν¬λ„ŒνŠΈ μž¬μ‚¬μš© ```javascript // κΈ°μ‘΄ μ‹œμŠ€ν…œ λž˜ν•‘ νŒ¨ν„΄ class ApiServiceWrapper { constructor() { // κΈ°μ‘΄ λ§€λ‹ˆμ €λ“€ κ·ΈλŒ€λ‘œ μ‚¬μš© this.prdManager = new PRDManager(); this.taskManager = new TaskManager(); this.documentManager = new DocumentManager(); // API μ „μš© κΈ°λŠ₯ μΆ”κ°€ this.helpService = new HelpService(); this.schemaService = new SchemaService(); this.exampleService = new ExampleService(); } } ``` ### 2. λ°μ΄ν„°λ² μ΄μŠ€ λ ˆμ΄μ–΄ 곡유 ``` κΈ°μ‘΄ SQLite DB μŠ€ν‚€λ§ˆ μœ μ§€ β”œβ”€β”€ prds (κΈ°μ‘΄) β”œβ”€β”€ tasks (κΈ°μ‘΄) β”œβ”€β”€ documents (κΈ°μ‘΄) β”œβ”€β”€ document_links (κΈ°μ‘΄) └── api_schemas (μ‹ κ·œ) - API μŠ€ν‚€λ§ˆ 정보 └── api_examples (μ‹ κ·œ) - API 예제 μ €μž₯ ``` ### 3. 이벀트 μ‹œμŠ€ν…œ 톡합 ```javascript // κΈ°μ‘΄ 이벀트 μ‹œμŠ€ν…œ ν™•μž₯ const EventEmitter = require('events'); class ApiEventManager extends EventEmitter { constructor() { super(); // κΈ°μ‘΄ 이벀트 μ—°κ²° this.on('prd:created', this.notifySSE); this.on('task:updated', this.notifySSE); this.on('document:created', this.notifySSE); } notifySSE(event) { // SSE ν΄λΌμ΄μ–ΈνŠΈλ“€μ—κ²Œ μ‹€μ‹œκ°„ μ•Œλ¦Ό this.sseService.broadcast(event); } } ``` ## πŸ”„ Help API 상세 섀계 ### 1. 동적 Help 생성 ```javascript // services/HelpService.js (μƒˆλ‘œ 생성) class HelpService { constructor() { this.helpContent = { 'getting-started': this.getGettingStartedGuide(), 'prds/overview': this.getPrdOverview(), 'prds/examples': this.getPrdExamples(), 'tasks/dependencies': this.getTaskDependencyGuide() }; } getGettingStartedGuide() { return { title: "WorkflowMCP API μ‹œμž‘ν•˜κΈ°", sections: [ { title: "1. API 탐색", content: "GET /api λ₯Ό ν˜ΈμΆœν•˜μ—¬ 전체 API ꡬ쑰λ₯Ό νŒŒμ•…ν•˜μ„Έμš”", example: "curl http://localhost:3301/api" }, { title: "2. μΉ΄ν…Œκ³ λ¦¬ 선택", content: "μ›ν•˜λŠ” κΈ°λŠ₯의 μΉ΄ν…Œκ³ λ¦¬λ₯Ό μ„ νƒν•˜μ„Έμš” (prds, tasks, documents λ“±)", example: "curl http://localhost:3301/api/prds" } ], next_steps: [ "/api/help/prds/overview", "/api/help/tasks/overview", "/api/help/documents/overview" ] }; } } ``` ### 2. 관리 메뉴별 Help API #### PRD 관리 Help ``` GET /api/help/prds/overview { "title": "PRD 관리 API κ°œμš”", "description": "Product Requirements Document 관리λ₯Ό μœ„ν•œ μ™„μ „ν•œ API", "quick_start": { "create_prd": "POST /api/prds", "list_prds": "GET /api/prds", "get_prd": "GET /api/prds/{id}" }, "common_workflows": [ { "name": "μƒˆ PRD 생성 및 λ¬Έμ„œ μ—°κ²°", "steps": [ "POST /api/prds - PRD 생성", "POST /api/documents - κ΄€λ ¨ λ¬Έμ„œ 생성", "POST /api/prds/{id}/documents - λ¬Έμ„œ μ—°κ²°" ] } ], "troubleshooting": { "400_errors": "ν•„μˆ˜ ν•„λ“œ 확인: title, description", "404_errors": "PRD ID 쑴재 μ—¬λΆ€ 확인", "409_errors": "쀑볡 제λͺ© λ°©μ§€" } } ``` ### 3. μ‹€μ‹œκ°„ Help μ—…λ°μ΄νŠΈ ```javascript // API μ‚¬μš© νŒ¨ν„΄ λΆ„μ„ν•˜μ—¬ λ§žμΆ€ν˜• 도움말 제곡 class SmartHelpService { trackApiUsage(endpoint, success, error) { // μ‚¬μš© νŒ¨ν„΄ 기둝 this.usageStats[endpoint] = { calls: (this.usageStats[endpoint]?.calls || 0) + 1, errors: success ? 0 : (this.usageStats[endpoint]?.errors || 0) + 1, lastError: error }; } getPersonalizedHelp(sessionId) { // μ„Έμ…˜λ³„ μ‚¬μš© 이λ ₯ 기반 맞좀 도움말 const usage = this.sessionUsage[sessionId]; return { suggestions: this.generateSuggestions(usage), common_errors: this.getCommonErrors(usage), next_recommended: this.getRecommendedApis(usage) }; } } ``` ## πŸš€ ν™•μž₯ κ°€λŠ₯ν•œ μ•„ν‚€ν…μ²˜ ### 1. ν”ŒλŸ¬κ·ΈμΈ μ•„ν‚€ν…μ²˜ ```javascript // Plugin System for API Extensions class ApiPluginManager { constructor() { this.plugins = new Map(); } registerPlugin(name, plugin) { // μƒˆλ‘œμš΄ API μΉ΄ν…Œκ³ λ¦¬ 동적 μΆ”κ°€ this.plugins.set(name, plugin); this.updateApiDiscovery(); } updateApiDiscovery() { // /api 응닡에 μƒˆ ν”ŒλŸ¬κ·ΈμΈ μΉ΄ν…Œκ³ λ¦¬ μΆ”κ°€ this.discoveryService.addCategory(plugin); } } ``` ### 2. μžλ™ μŠ€ν‚€λ§ˆ 생성 ```javascript // κΈ°μ‘΄ MCP 도ꡬ λΆ„μ„ν•˜μ—¬ μžλ™ μŠ€ν‚€λ§ˆ 생성 class SchemaGenerator { generateFromMcpTool(toolName) { const tool = this.mcpTools[toolName]; return { input_schema: this.extractInputSchema(tool), output_schema: this.extractOutputSchema(tool), examples: this.generateExamples(tool) }; } } ``` ### 3. 버전 관리 μ‹œμŠ€ν…œ ```javascript // API 버전 관리 const versionedRouter = { 'v1': require('./routes/v1'), 'v2': require('./routes/v2') // ν–₯ν›„ ν™•μž₯ }; app.use('/api/:version', (req, res, next) => { const version = req.params.version; if (versionedRouter[version]) { versionedRouter[version](req, res, next); } else { res.status(404).json({ error: 'API version not found', available_versions: Object.keys(versionedRouter), help: '/api/help/versioning' }); } }); ``` ## πŸ“‘ μ‹€μ‹œκ°„ 톡신 μ•„ν‚€ν…μ²˜ ### 1. Server-Sent Events (SSE) ```javascript // SSE μ„œλΉ„μŠ€ ꡬ쑰 class SSEService { constructor() { this.connections = new Map(); // sessionId -> response object this.eventFilters = new Map(); // sessionId -> filter config } subscribe(req, res) { const sessionId = req.headers['x-session-id'] || this.generateSessionId(); res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*' }); this.connections.set(sessionId, res); // μ—°κ²° 확인 λ©”μ‹œμ§€ this.sendEvent(sessionId, { type: 'connected', data: { message: 'SSE connection established' } }); } broadcast(event) { this.connections.forEach((res, sessionId) => { if (this.shouldSendEvent(sessionId, event)) { this.sendEvent(sessionId, event); } }); } } ``` ### 2. WebSocket 지원 (ν–₯ν›„ ν™•μž₯) ```javascript // WebSocket 지원 μ€€λΉ„ class WebSocketService { constructor() { this.wss = null; // ν–₯ν›„ WebSocket μ„œλ²„ } upgrade(server) { // HTTP β†’ WebSocket μ—…κ·Έλ ˆμ΄λ“œ 지원 this.wss = new WebSocketServer({ server }); } } ``` ## πŸ”’ λ³΄μ•ˆ μ•„ν‚€ν…μ²˜ ### 1. μž…λ ₯ 검증 미듀웨어 ```javascript // λͺ¨λ“  API μž…λ ₯ 검증 const validationMiddleware = { validatePrdInput: (req, res, next) => { const schema = joi.object({ title: joi.string().min(1).max(200).required(), description: joi.string().min(1).required(), requirements: joi.array().items(joi.string()), priority: joi.string().valid('high', 'medium', 'low') }); const { error } = schema.validate(req.body); if (error) { return res.status(400).json({ success: false, error: { code: 'VALIDATION_ERROR', message: 'Invalid input data', details: error.details.map(d => d.message) } }); } next(); } }; ``` ### 2. Rate Limiting ```javascript // API별 Rate Limiting const rateLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15λΆ„ max: 1000, // μ΅œλŒ€ 1000 μš”μ²­ message: { success: false, error: { code: 'RATE_LIMIT_EXCEEDED', message: 'Too many requests', help: '/api/help/rate-limits' } } }); ``` ## πŸ“ˆ μ„±λŠ₯ μ΅œμ ν™” μ•„ν‚€ν…μ²˜ ### 1. 캐싱 λ ˆμ΄μ–΄ ```javascript // λ‹€μΈ΅ 캐싱 μ‹œμŠ€ν…œ class CacheManager { constructor() { this.memoryCache = new Map(); // λ©”λͺ¨λ¦¬ μΊμ‹œ this.queryCache = new Map(); // 쿼리 κ²°κ³Ό μΊμ‹œ } async get(key, fetcher) { // 1. λ©”λͺ¨λ¦¬ μΊμ‹œ 확인 if (this.memoryCache.has(key)) { return this.memoryCache.get(key); } // 2. 데이터 페치 및 μΊμ‹œ μ €μž₯ const data = await fetcher(); this.memoryCache.set(key, data); return data; } invalidate(pattern) { // νŒ¨ν„΄ 기반 μΊμ‹œ λ¬΄νš¨ν™” for (const key of this.memoryCache.keys()) { if (key.includes(pattern)) { this.memoryCache.delete(key); } } } } ``` ### 2. 쿼리 μ΅œμ ν™” ```javascript // κΈ°μ‘΄ λ§€λ‹ˆμ € 쿼리 μ΅œμ ν™” 래퍼 class OptimizedQueryWrapper { constructor(manager) { this.manager = manager; this.queryCache = new Map(); } async optimizedList(filters) { const cacheKey = JSON.stringify(filters); if (this.queryCache.has(cacheKey)) { return this.queryCache.get(cacheKey); } const result = await this.manager.list(filters); this.queryCache.set(cacheKey, result); // 5λΆ„ ν›„ μΊμ‹œ 만료 setTimeout(() => { this.queryCache.delete(cacheKey); }, 5 * 60 * 1000); return result; } } ``` ## πŸ”§ 개발자 도ꡬ μ•„ν‚€ν…μ²˜ ### 1. API ν…ŒμŠ€νŠΈ μ½˜μ†” ```javascript // λ‚΄μž₯ API ν…ŒμŠ€νŠΈ 도ꡬ app.get('/api/console', (req, res) => { res.send(` <!DOCTYPE html> <html> <head> <title>WorkflowMCP API Console</title> <style>/* API ν…ŒμŠ€νŠΈ UI μŠ€νƒ€μΌ */</style> </head> <body> <div id="api-console"> <!-- λŒ€ν™”ν˜• API ν…ŒμŠ€νŠΈ μΈν„°νŽ˜μ΄μŠ€ --> </div> <script> // API 호좜 및 응닡 ν‘œμ‹œ 둜직 </script> </body> </html> `); }); ``` ### 2. μžλ™ λ¬Έμ„œ 생성 ```javascript // OpenAPI/Swagger μžλ™ 생성 class ApiDocGenerator { generateOpenApiSpec() { return { openapi: '3.0.0', info: { title: 'WorkflowMCP Dashboard API', version: '1.0.0', description: 'Complete API for WorkflowMCP system' }, paths: this.generatePaths(), components: { schemas: this.generateSchemas() } }; } } ``` --- **μž‘μ„±μΌ**: 2025-09-11 **μž‘μ„±μž**: μ‹œμŠ€ν…œ μ„€κ³„μž (Claude Code) **κ²€ν† μž**: μš”κ΅¬μ‚¬ν•­ 뢄석가, 개발자 **버전**: 1.0 **μƒνƒœ**: λ°μ΄ν„°λ² μ΄μŠ€ 섀계 λŒ€κΈ°

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/foswmine/workflow-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server