ai-playbook-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/package.json +21 -0
- package/src/index.js +124 -0
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# ai-playbook-mcp
|
|
2
|
+
|
|
3
|
+
MCP client for ai-playbook. Connects AI coding tools (Kiro, Claude Code, etc.) to your playbook assets.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
No manual installation needed. Use with npx:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
// ~/.kiro/settings/mcp.json
|
|
11
|
+
{
|
|
12
|
+
"mcpServers": {
|
|
13
|
+
"ai-playbook": {
|
|
14
|
+
"command": "npx",
|
|
15
|
+
"args": ["ai-playbook-mcp"],
|
|
16
|
+
"env": {
|
|
17
|
+
"PLAYBOOK_API_URL": "https://your-domain.com",
|
|
18
|
+
"PLAYBOOK_API_KEY": "your-api-key"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Environment Variables
|
|
26
|
+
|
|
27
|
+
| Variable | Required | Description |
|
|
28
|
+
|----------|----------|-------------|
|
|
29
|
+
| PLAYBOOK_API_URL | Yes | ai-playbook HTTP API server URL |
|
|
30
|
+
| PLAYBOOK_API_KEY | No | API authentication key |
|
|
31
|
+
|
|
32
|
+
## Tools Provided
|
|
33
|
+
|
|
34
|
+
| Tool | Description |
|
|
35
|
+
|------|-------------|
|
|
36
|
+
| list_catalog | List all assets and groups (with optional type/tags filter) |
|
|
37
|
+
| load_asset | Load full asset content (frontmatter + body) |
|
|
38
|
+
| suggest_assets | Suggest relevant assets based on task description |
|
|
39
|
+
| get_group | Get group details with included assets |
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ai-playbook-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP client for ai-playbook — connects AI coding tools to your playbook assets",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ai-playbook-mcp": "./src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": ["src/"],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node src/index.js"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
19
|
+
"keywords": ["mcp", "ai", "playbook", "kiro", "claude", "cursor"],
|
|
20
|
+
"license": "MIT"
|
|
21
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
|
|
4
|
+
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
5
|
+
const { ListToolsRequestSchema, CallToolRequestSchema } = require('@modelcontextprotocol/sdk/types.js');
|
|
6
|
+
|
|
7
|
+
const API_URL = process.env.PLAYBOOK_API_URL || 'http://localhost:3100';
|
|
8
|
+
const API_KEY = process.env.PLAYBOOK_API_KEY || '';
|
|
9
|
+
|
|
10
|
+
async function fetchApi(path) {
|
|
11
|
+
const url = `${API_URL}${path}`;
|
|
12
|
+
const headers = {};
|
|
13
|
+
if (API_KEY) {
|
|
14
|
+
headers['Authorization'] = `Bearer ${API_KEY}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const response = await fetch(url, { headers });
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
const error = await response.json().catch(() => ({ error: response.statusText }));
|
|
20
|
+
throw new Error(error.error || `HTTP ${response.status}`);
|
|
21
|
+
}
|
|
22
|
+
return response.json();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const server = new Server(
|
|
26
|
+
{ name: 'ai-playbook', version: '1.0.0' },
|
|
27
|
+
{ capabilities: { tools: {} } }
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// Tool: list_catalog
|
|
31
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
32
|
+
tools: [
|
|
33
|
+
{
|
|
34
|
+
name: 'list_catalog',
|
|
35
|
+
description: 'ai-playbook 자산 목록 조회. 세션 시작 시 호출하여 사용 가능한 자산과 그룹을 확인한다. type이나 tags로 필터 가능.',
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
type: { type: 'string', description: '자산 타입 필터 (rule, workflow, agent, automation, skill)', enum: ['rule', 'workflow', 'agent', 'automation', 'skill'] },
|
|
40
|
+
tags: { type: 'string', description: '태그 필터 (쉼표 구분)' }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'load_asset',
|
|
46
|
+
description: '특정 자산의 전체 내용(frontmatter + 본문)을 로드한다. 프로젝트에 다운로드하거나 참조할 때 사용.',
|
|
47
|
+
inputSchema: {
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: {
|
|
50
|
+
id: { type: 'string', description: '자산 ID' }
|
|
51
|
+
},
|
|
52
|
+
required: ['id']
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'suggest_assets',
|
|
57
|
+
description: '작업 설명을 기반으로 관련 자산을 추천한다. 키워드 매칭으로 상위 5개 반환.',
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
task_description: { type: 'string', description: '현재 작업 설명' }
|
|
62
|
+
},
|
|
63
|
+
required: ['task_description']
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'get_group',
|
|
68
|
+
description: '자산 그룹의 상세 정보를 조회한다. 그룹에 포함된 자산 목록과 설명 반환.',
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {
|
|
72
|
+
group_id: { type: 'string', description: '그룹 ID (java-backend, react-frontend, common-rules 등)' }
|
|
73
|
+
},
|
|
74
|
+
required: ['group_id']
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
// Tool execution
|
|
82
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
83
|
+
const { name, arguments: args } = request.params;
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
switch (name) {
|
|
87
|
+
case 'list_catalog': {
|
|
88
|
+
const params = new URLSearchParams();
|
|
89
|
+
if (args.type) params.set('type', args.type);
|
|
90
|
+
if (args.tags) params.set('tags', args.tags);
|
|
91
|
+
const query = params.toString() ? `?${params.toString()}` : '';
|
|
92
|
+
const data = await fetchApi(`/api/catalog${query}`);
|
|
93
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
case 'load_asset': {
|
|
97
|
+
const data = await fetchApi(`/api/asset/${args.id}`);
|
|
98
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
case 'suggest_assets': {
|
|
102
|
+
const data = await fetchApi(`/api/suggest?q=${encodeURIComponent(args.task_description)}`);
|
|
103
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
case 'get_group': {
|
|
107
|
+
const data = await fetchApi(`/api/group/${args.group_id}`);
|
|
108
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
default:
|
|
112
|
+
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true };
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
async function main() {
|
|
120
|
+
const transport = new StdioServerTransport();
|
|
121
|
+
await server.connect(transport);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
main().catch(console.error);
|