# ποΈ μμ€ν
μν€ν
μ² - 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
**μν**: λ°μ΄ν°λ² μ΄μ€ μ€κ³ λκΈ°