@geekbeer/minion 2.59.0 → 2.62.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/core/lib/permissions.js +279 -0
- package/core/routes/permissions.js +93 -0
- package/docs/api-reference.md +40 -0
- package/docs/environment-setup.md +75 -7
- package/linux/server.js +2 -0
- package/package.json +1 -1
- package/rules/core.md +66 -1
- package/win/server.js +2 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Permission Abstraction Layer
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified interface for reading/writing permission configurations
|
|
5
|
+
* across multiple AI coding CLI tools:
|
|
6
|
+
* - Claude Code: ~/.claude/settings.local.json (JSON)
|
|
7
|
+
* - Gemini CLI: ~/.gemini/settings.json (JSON)
|
|
8
|
+
* - Codex CLI: ~/.codex/config.toml (TOML)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs')
|
|
12
|
+
const path = require('path')
|
|
13
|
+
|
|
14
|
+
// ── CLI Definitions ──────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
const CLI_DEFS = {
|
|
17
|
+
'claude-code': {
|
|
18
|
+
label: 'Claude Code',
|
|
19
|
+
configDir: '.claude',
|
|
20
|
+
// Read from settings.json (bundled sync target) + merge settings.local.json (user overrides)
|
|
21
|
+
readFiles: ['settings.json', 'settings.local.json'],
|
|
22
|
+
// Write to settings.local.json to survive server restarts (settings.json is overwritten by syncPermissions)
|
|
23
|
+
writeFile: 'settings.local.json',
|
|
24
|
+
format: 'json',
|
|
25
|
+
},
|
|
26
|
+
'gemini': {
|
|
27
|
+
label: 'Gemini CLI',
|
|
28
|
+
configDir: '.gemini',
|
|
29
|
+
readFiles: ['settings.json'],
|
|
30
|
+
writeFile: 'settings.json',
|
|
31
|
+
format: 'json',
|
|
32
|
+
},
|
|
33
|
+
'codex': {
|
|
34
|
+
label: 'Codex CLI',
|
|
35
|
+
configDir: '.codex',
|
|
36
|
+
readFiles: ['config.toml'],
|
|
37
|
+
writeFile: 'config.toml',
|
|
38
|
+
format: 'toml',
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── TOML Helpers (minimal, for Codex config.toml) ────────────────────────────
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parse a minimal TOML file to extract permissions.allow and permissions.deny arrays.
|
|
46
|
+
* Only supports the subset needed for Codex CLI config.
|
|
47
|
+
*/
|
|
48
|
+
function parseTomlPermissions(content) {
|
|
49
|
+
const allow = []
|
|
50
|
+
const deny = []
|
|
51
|
+
|
|
52
|
+
let currentSection = ''
|
|
53
|
+
for (const rawLine of content.split('\n')) {
|
|
54
|
+
const line = rawLine.trim()
|
|
55
|
+
if (!line || line.startsWith('#')) continue
|
|
56
|
+
|
|
57
|
+
// Section header: [permissions] or [permissions.default] etc.
|
|
58
|
+
const sectionMatch = line.match(/^\[([^\]]+)\]$/)
|
|
59
|
+
if (sectionMatch) {
|
|
60
|
+
currentSection = sectionMatch[1]
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (currentSection !== 'permissions') continue
|
|
65
|
+
|
|
66
|
+
// Key = value
|
|
67
|
+
const kvMatch = line.match(/^(\w+)\s*=\s*(.+)$/)
|
|
68
|
+
if (!kvMatch) continue
|
|
69
|
+
const [, key, rawValue] = kvMatch
|
|
70
|
+
|
|
71
|
+
if (key === 'allow' || key === 'deny') {
|
|
72
|
+
const arr = parseTomlArray(rawValue)
|
|
73
|
+
if (key === 'allow') allow.push(...arr)
|
|
74
|
+
else deny.push(...arr)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { allow, deny }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Parse a TOML inline array: ["a", "b", "c"]
|
|
83
|
+
*/
|
|
84
|
+
function parseTomlArray(raw) {
|
|
85
|
+
const trimmed = raw.trim()
|
|
86
|
+
if (!trimmed.startsWith('[') || !trimmed.endsWith(']')) return []
|
|
87
|
+
const inner = trimmed.slice(1, -1)
|
|
88
|
+
const items = []
|
|
89
|
+
// Match quoted strings
|
|
90
|
+
const re = /"([^"]*?)"|'([^']*?)'/g
|
|
91
|
+
let m
|
|
92
|
+
while ((m = re.exec(inner)) !== null) {
|
|
93
|
+
items.push(m[1] ?? m[2])
|
|
94
|
+
}
|
|
95
|
+
return items
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Write permissions into a TOML file, preserving non-permissions content.
|
|
100
|
+
*/
|
|
101
|
+
function writeTomlPermissions(existingContent, { allow, deny }) {
|
|
102
|
+
const lines = existingContent ? existingContent.split('\n') : []
|
|
103
|
+
const outputLines = []
|
|
104
|
+
let inPermissionsSection = false
|
|
105
|
+
let permissionsWritten = false
|
|
106
|
+
|
|
107
|
+
for (const line of lines) {
|
|
108
|
+
const trimmed = line.trim()
|
|
109
|
+
const sectionMatch = trimmed.match(/^\[([^\]]+)\]$/)
|
|
110
|
+
|
|
111
|
+
if (sectionMatch) {
|
|
112
|
+
if (sectionMatch[1] === 'permissions') {
|
|
113
|
+
inPermissionsSection = true
|
|
114
|
+
// Write our new permissions section
|
|
115
|
+
outputLines.push('[permissions]')
|
|
116
|
+
outputLines.push(`allow = [${allow.map(s => `"${s}"`).join(', ')}]`)
|
|
117
|
+
outputLines.push(`deny = [${deny.map(s => `"${s}"`).join(', ')}]`)
|
|
118
|
+
permissionsWritten = true
|
|
119
|
+
continue
|
|
120
|
+
} else {
|
|
121
|
+
inPermissionsSection = false
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (inPermissionsSection) continue // skip old permission lines
|
|
126
|
+
outputLines.push(line)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// If no existing [permissions] section, append one
|
|
130
|
+
if (!permissionsWritten) {
|
|
131
|
+
if (outputLines.length > 0 && outputLines[outputLines.length - 1] !== '') {
|
|
132
|
+
outputLines.push('')
|
|
133
|
+
}
|
|
134
|
+
outputLines.push('[permissions]')
|
|
135
|
+
outputLines.push(`allow = [${allow.map(s => `"${s}"`).join(', ')}]`)
|
|
136
|
+
outputLines.push(`deny = [${deny.map(s => `"${s}"`).join(', ')}]`)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return outputLines.join('\n')
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── JSON Helpers ─────────────────────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
function readJsonPermissions(filePath) {
|
|
145
|
+
try {
|
|
146
|
+
const content = fs.readFileSync(filePath, 'utf-8')
|
|
147
|
+
const data = JSON.parse(content)
|
|
148
|
+
const perms = data.permissions || {}
|
|
149
|
+
return {
|
|
150
|
+
allow: Array.isArray(perms.allow) ? perms.allow : [],
|
|
151
|
+
deny: Array.isArray(perms.deny) ? perms.deny : [],
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
return { allow: [], deny: [] }
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function writeJsonPermissions(filePath, { allow, deny }) {
|
|
159
|
+
let data = {}
|
|
160
|
+
try {
|
|
161
|
+
data = JSON.parse(fs.readFileSync(filePath, 'utf-8'))
|
|
162
|
+
} catch {
|
|
163
|
+
// File doesn't exist or is invalid — start fresh
|
|
164
|
+
}
|
|
165
|
+
data.permissions = { allow, deny }
|
|
166
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
167
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8')
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Detect which CLIs have config directories present.
|
|
174
|
+
* @param {string} homeDir
|
|
175
|
+
* @returns {string[]} Array of cli_type strings
|
|
176
|
+
*/
|
|
177
|
+
function detectInstalledClis(homeDir) {
|
|
178
|
+
return Object.entries(CLI_DEFS)
|
|
179
|
+
.filter(([, def]) => fs.existsSync(path.join(homeDir, def.configDir)))
|
|
180
|
+
.map(([type]) => type)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Read effective permissions for a specific CLI.
|
|
185
|
+
* For Claude Code, merges settings.json + settings.local.json.
|
|
186
|
+
* @param {string} homeDir
|
|
187
|
+
* @param {string} cliType
|
|
188
|
+
* @returns {{ cli_type: string, label: string, allow: string[], deny: string[], config_path: string } | null}
|
|
189
|
+
*/
|
|
190
|
+
function getPermissions(homeDir, cliType) {
|
|
191
|
+
const def = CLI_DEFS[cliType]
|
|
192
|
+
if (!def) return null
|
|
193
|
+
|
|
194
|
+
const configDir = path.join(homeDir, def.configDir)
|
|
195
|
+
if (!fs.existsSync(configDir)) return null
|
|
196
|
+
|
|
197
|
+
let mergedAllow = []
|
|
198
|
+
let mergedDeny = []
|
|
199
|
+
|
|
200
|
+
for (const file of def.readFiles) {
|
|
201
|
+
const filePath = path.join(configDir, file)
|
|
202
|
+
if (!fs.existsSync(filePath)) continue
|
|
203
|
+
|
|
204
|
+
if (def.format === 'json') {
|
|
205
|
+
const perms = readJsonPermissions(filePath)
|
|
206
|
+
// Later files override earlier ones (settings.local.json overrides settings.json)
|
|
207
|
+
if (perms.allow.length > 0 || perms.deny.length > 0) {
|
|
208
|
+
mergedAllow = perms.allow
|
|
209
|
+
mergedDeny = perms.deny
|
|
210
|
+
}
|
|
211
|
+
} else if (def.format === 'toml') {
|
|
212
|
+
try {
|
|
213
|
+
const content = fs.readFileSync(filePath, 'utf-8')
|
|
214
|
+
const perms = parseTomlPermissions(content)
|
|
215
|
+
if (perms.allow.length > 0 || perms.deny.length > 0) {
|
|
216
|
+
mergedAllow = perms.allow
|
|
217
|
+
mergedDeny = perms.deny
|
|
218
|
+
}
|
|
219
|
+
} catch {
|
|
220
|
+
// Skip unreadable files
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
cli_type: cliType,
|
|
227
|
+
label: def.label,
|
|
228
|
+
allow: mergedAllow,
|
|
229
|
+
deny: mergedDeny,
|
|
230
|
+
config_path: path.join(configDir, def.writeFile),
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Read permissions for all detected CLIs.
|
|
236
|
+
* @param {string} homeDir
|
|
237
|
+
* @returns {Array<{ cli_type: string, label: string, allow: string[], deny: string[], config_path: string }>}
|
|
238
|
+
*/
|
|
239
|
+
function getAllPermissions(homeDir) {
|
|
240
|
+
const results = []
|
|
241
|
+
for (const cliType of Object.keys(CLI_DEFS)) {
|
|
242
|
+
const perms = getPermissions(homeDir, cliType)
|
|
243
|
+
if (perms) results.push(perms)
|
|
244
|
+
}
|
|
245
|
+
return results
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Update permissions for a specific CLI.
|
|
250
|
+
* @param {string} homeDir
|
|
251
|
+
* @param {string} cliType
|
|
252
|
+
* @param {{ allow: string[], deny: string[] }} permissions
|
|
253
|
+
*/
|
|
254
|
+
function setPermissions(homeDir, cliType, { allow, deny }) {
|
|
255
|
+
const def = CLI_DEFS[cliType]
|
|
256
|
+
if (!def) throw new Error(`Unsupported CLI type: ${cliType}`)
|
|
257
|
+
|
|
258
|
+
const configDir = path.join(homeDir, def.configDir)
|
|
259
|
+
const filePath = path.join(configDir, def.writeFile)
|
|
260
|
+
|
|
261
|
+
fs.mkdirSync(configDir, { recursive: true })
|
|
262
|
+
|
|
263
|
+
if (def.format === 'json') {
|
|
264
|
+
writeJsonPermissions(filePath, { allow, deny })
|
|
265
|
+
} else if (def.format === 'toml') {
|
|
266
|
+
let existing = ''
|
|
267
|
+
try { existing = fs.readFileSync(filePath, 'utf-8') } catch {}
|
|
268
|
+
const newContent = writeTomlPermissions(existing, { allow, deny })
|
|
269
|
+
fs.writeFileSync(filePath, newContent, 'utf-8')
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
module.exports = {
|
|
274
|
+
detectInstalledClis,
|
|
275
|
+
getPermissions,
|
|
276
|
+
getAllPermissions,
|
|
277
|
+
setPermissions,
|
|
278
|
+
CLI_DEFS,
|
|
279
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission management endpoints
|
|
3
|
+
*
|
|
4
|
+
* Provides read/write access to CLI tool permission configurations.
|
|
5
|
+
* Supports Claude Code, Gemini CLI, and Codex CLI.
|
|
6
|
+
*
|
|
7
|
+
* Endpoints:
|
|
8
|
+
* GET /api/config/permissions - Get permissions for all detected CLIs
|
|
9
|
+
* GET /api/config/permissions/:cli_type - Get permissions for a specific CLI
|
|
10
|
+
* POST /api/config/permissions - Update permissions for a specific CLI
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { verifyToken } = require('../lib/auth')
|
|
14
|
+
const { config } = require('../config')
|
|
15
|
+
const { getAllPermissions, getPermissions, setPermissions, CLI_DEFS } = require('../lib/permissions')
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Register permission routes as Fastify plugin
|
|
19
|
+
* @param {import('fastify').FastifyInstance} fastify
|
|
20
|
+
*/
|
|
21
|
+
async function permissionRoutes(fastify) {
|
|
22
|
+
|
|
23
|
+
// GET /api/config/permissions - Get permissions for all detected CLIs
|
|
24
|
+
fastify.get('/api/config/permissions', async (request, reply) => {
|
|
25
|
+
if (!verifyToken(request)) {
|
|
26
|
+
reply.code(401)
|
|
27
|
+
return { success: false, error: 'Unauthorized' }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const permissions = getAllPermissions(config.HOME_DIR)
|
|
31
|
+
return { success: true, permissions }
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// GET /api/config/permissions/:cli_type - Get permissions for a specific CLI
|
|
35
|
+
fastify.get('/api/config/permissions/:cli_type', async (request, reply) => {
|
|
36
|
+
if (!verifyToken(request)) {
|
|
37
|
+
reply.code(401)
|
|
38
|
+
return { success: false, error: 'Unauthorized' }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { cli_type } = request.params
|
|
42
|
+
if (!CLI_DEFS[cli_type]) {
|
|
43
|
+
reply.code(400)
|
|
44
|
+
return { success: false, error: `Unsupported CLI type: ${cli_type}. Supported: ${Object.keys(CLI_DEFS).join(', ')}` }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const perms = getPermissions(config.HOME_DIR, cli_type)
|
|
48
|
+
if (!perms) {
|
|
49
|
+
reply.code(404)
|
|
50
|
+
return { success: false, error: `CLI ${cli_type} is not installed on this minion` }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { success: true, ...perms }
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// POST /api/config/permissions - Update permissions for a specific CLI
|
|
57
|
+
fastify.post('/api/config/permissions', async (request, reply) => {
|
|
58
|
+
if (!verifyToken(request)) {
|
|
59
|
+
reply.code(401)
|
|
60
|
+
return { success: false, error: 'Unauthorized' }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { cli_type, allow, deny } = request.body || {}
|
|
64
|
+
|
|
65
|
+
if (!cli_type) {
|
|
66
|
+
reply.code(400)
|
|
67
|
+
return { success: false, error: 'cli_type is required' }
|
|
68
|
+
}
|
|
69
|
+
if (!CLI_DEFS[cli_type]) {
|
|
70
|
+
reply.code(400)
|
|
71
|
+
return { success: false, error: `Unsupported CLI type: ${cli_type}. Supported: ${Object.keys(CLI_DEFS).join(', ')}` }
|
|
72
|
+
}
|
|
73
|
+
if (!Array.isArray(allow) || !Array.isArray(deny)) {
|
|
74
|
+
reply.code(400)
|
|
75
|
+
return { success: false, error: 'allow and deny must be arrays' }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
setPermissions(config.HOME_DIR, cli_type, { allow, deny })
|
|
80
|
+
|
|
81
|
+
// Read back effective permissions to confirm
|
|
82
|
+
const updated = getPermissions(config.HOME_DIR, cli_type)
|
|
83
|
+
console.log(`[Permissions] Updated ${cli_type}: allow=${allow.length}, deny=${deny.length}`)
|
|
84
|
+
|
|
85
|
+
return { success: true, ...updated }
|
|
86
|
+
} catch (err) {
|
|
87
|
+
reply.code(500)
|
|
88
|
+
return { success: false, error: err.message }
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { permissionRoutes }
|
package/docs/api-reference.md
CHANGED
|
@@ -204,6 +204,46 @@ Changes via the config API take effect immediately (no restart required).
|
|
|
204
204
|
|
|
205
205
|
Allowed keys: `LLM_COMMAND`, `REFLECTION_TIME`
|
|
206
206
|
|
|
207
|
+
### Permissions
|
|
208
|
+
|
|
209
|
+
CLIツール(Claude Code, Gemini CLI, Codex CLI)のパーミッション管理。
|
|
210
|
+
CLIツールは自身の設定ファイルを直接編集できないため、このAPIを経由して更新する。
|
|
211
|
+
|
|
212
|
+
| Method | Endpoint | Description |
|
|
213
|
+
|--------|----------|-------------|
|
|
214
|
+
| GET | `/api/config/permissions` | Get permissions for all detected CLIs |
|
|
215
|
+
| GET | `/api/config/permissions/:cli_type` | Get permissions for a specific CLI |
|
|
216
|
+
| POST | `/api/config/permissions` | Update permissions. Body: `{cli_type, allow, deny}` |
|
|
217
|
+
|
|
218
|
+
Supported `cli_type`: `claude-code`, `gemini`, `codex`
|
|
219
|
+
|
|
220
|
+
**GET response**:
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"success": true,
|
|
224
|
+
"permissions": [
|
|
225
|
+
{
|
|
226
|
+
"cli_type": "claude-code",
|
|
227
|
+
"label": "Claude Code",
|
|
228
|
+
"allow": ["Bash", "Read", "Write", "Edit"],
|
|
229
|
+
"deny": ["Bash(sudo *)"],
|
|
230
|
+
"config_path": "/home/minion/.claude/settings.local.json"
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**POST body**:
|
|
237
|
+
```json
|
|
238
|
+
{
|
|
239
|
+
"cli_type": "claude-code",
|
|
240
|
+
"allow": ["Bash", "Read", "Write", "Edit", "WebSearch"],
|
|
241
|
+
"deny": ["Bash(sudo *)", "Bash(rm -rf *)"]
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Note: Claude Code の場合、書き込み先は `settings.local.json`(サーバー再起動時に上書きされない)。
|
|
246
|
+
|
|
207
247
|
### Commands
|
|
208
248
|
|
|
209
249
|
| Method | Endpoint | Description |
|
|
@@ -28,9 +28,20 @@ requires:
|
|
|
28
28
|
|
|
29
29
|
## MCP サーバーの設定
|
|
30
30
|
|
|
31
|
-
MCP
|
|
31
|
+
MCPサーバーの設定ファイルは使用するCLIツールによって異なる。
|
|
32
|
+
いずれも LLM から直接編集可能(パーミッション設定とは異なり、API経由は不要)。
|
|
32
33
|
|
|
33
|
-
###
|
|
34
|
+
### CLI別の設定ファイル
|
|
35
|
+
|
|
36
|
+
| CLI | 設定ファイル | フォーマット | 編集方法 |
|
|
37
|
+
|-----|------------|------------|---------|
|
|
38
|
+
| Claude Code | `.mcp.json`(プロジェクトルート) | JSON | Edit/Write ツールで直接編集 |
|
|
39
|
+
| Gemini CLI | `.gemini/settings.json` | JSON | Edit/Write ツールで直接編集 |
|
|
40
|
+
| Codex CLI | `~/.codex/config.toml` | TOML | `codex mcp add/remove` コマンド推奨 |
|
|
41
|
+
|
|
42
|
+
### Claude Code
|
|
43
|
+
|
|
44
|
+
設定ファイル: `.mcp.json`(プロジェクトルート)または `~/.mcp.json`(ユーザーレベル)
|
|
34
45
|
|
|
35
46
|
```json
|
|
36
47
|
{
|
|
@@ -43,9 +54,8 @@ MCP サーバーは `~/.mcp.json` に JSON 形式で設定する。このファ
|
|
|
43
54
|
}
|
|
44
55
|
```
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
1. `~/.mcp.json` が存在しない場合は新規作成する
|
|
57
|
+
**設定の追加・変更手順:**
|
|
58
|
+
1. `.mcp.json` が存在しない場合は新規作成する
|
|
49
59
|
2. 既存の場合は内容を読み取り、`mcpServers` オブジェクトにエントリを追加する
|
|
50
60
|
3. 既存エントリを壊さないよう注意する
|
|
51
61
|
|
|
@@ -54,10 +64,54 @@ MCP サーバーは `~/.mcp.json` に JSON 形式で設定する。このファ
|
|
|
54
64
|
cat ~/.mcp.json 2>/dev/null || echo '(not found)'
|
|
55
65
|
```
|
|
56
66
|
|
|
67
|
+
**スコープの優先順:**
|
|
68
|
+
1. ローカル (`~/.claude.json` 内のプロジェクト固有設定) — 最優先
|
|
69
|
+
2. プロジェクト (`.mcp.json`) — チーム共有
|
|
70
|
+
3. ユーザー (`~/.claude.json`) — 全プロジェクト共通
|
|
71
|
+
|
|
72
|
+
### Gemini CLI
|
|
73
|
+
|
|
74
|
+
設定ファイル: `.gemini/settings.json`(プロジェクト)または `~/.gemini/settings.json`(ユーザーレベル)
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"<server-name>": {
|
|
80
|
+
"command": "<起動コマンド>",
|
|
81
|
+
"args": ["<引数1>", "<引数2>"],
|
|
82
|
+
"timeout": 30000
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
追加フィールド: `cwd`(作業ディレクトリ)、`timeout`(ミリ秒)、`trust`(確認プロンプト省略)、`includeTools`/`excludeTools`(ツールフィルタ)
|
|
89
|
+
|
|
90
|
+
### Codex CLI
|
|
91
|
+
|
|
92
|
+
設定ファイル: `.codex/config.toml`(プロジェクト)または `~/.codex/config.toml`(ユーザーレベル)
|
|
93
|
+
|
|
94
|
+
```toml
|
|
95
|
+
[mcp_servers.<server-name>]
|
|
96
|
+
command = "<起動コマンド>"
|
|
97
|
+
args = ["<引数1>", "<引数2>"]
|
|
98
|
+
startup_timeout_sec = 10.0
|
|
99
|
+
tool_timeout_sec = 60.0
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**注意:** `.codex/` ディレクトリは LLM からの直接編集が制限される場合がある。以下のコマンドを使用すること:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
codex mcp add <name> --command "<command>" --args "<arg1>,<arg2>"
|
|
106
|
+
codex mcp remove <name>
|
|
107
|
+
codex mcp list
|
|
108
|
+
```
|
|
109
|
+
|
|
57
110
|
### よく使う MCP サーバーの設定例
|
|
58
111
|
|
|
59
112
|
#### Playwright(ブラウザ自動化)
|
|
60
113
|
|
|
114
|
+
**Claude Code / Gemini CLI:**
|
|
61
115
|
```json
|
|
62
116
|
{
|
|
63
117
|
"mcpServers": {
|
|
@@ -69,10 +123,18 @@ cat ~/.mcp.json 2>/dev/null || echo '(not found)'
|
|
|
69
123
|
}
|
|
70
124
|
```
|
|
71
125
|
|
|
126
|
+
**Codex CLI:**
|
|
127
|
+
```toml
|
|
128
|
+
[mcp_servers.playwright]
|
|
129
|
+
command = "npx"
|
|
130
|
+
args = ["-y", "@playwright/mcp@latest"]
|
|
131
|
+
```
|
|
132
|
+
|
|
72
133
|
`npx -y` により、未インストールでも自動ダウンロード・実行される。事前の `npm install` は不要。
|
|
73
134
|
|
|
74
135
|
#### Supabase(データベース)
|
|
75
136
|
|
|
137
|
+
**Claude Code / Gemini CLI:**
|
|
76
138
|
```json
|
|
77
139
|
{
|
|
78
140
|
"mcpServers": {
|
|
@@ -83,14 +145,20 @@ cat ~/.mcp.json 2>/dev/null || echo '(not found)'
|
|
|
83
145
|
}
|
|
84
146
|
```
|
|
85
147
|
|
|
148
|
+
**Codex CLI:**
|
|
149
|
+
```toml
|
|
150
|
+
[mcp_servers.supabase]
|
|
151
|
+
url = "http://<supabase-host>:54321/mcp?read_only=true&features=database,docs"
|
|
152
|
+
```
|
|
153
|
+
|
|
86
154
|
URL ベースの MCP サーバーは `url` フィールドで指定する(`command`/`args` は不要)。
|
|
87
155
|
|
|
88
156
|
### 注意事項
|
|
89
157
|
|
|
90
|
-
-
|
|
158
|
+
- MCP設定ファイルはLLMから直接編集できる(パーミッション設定ファイルとは異なり書き込み保護がない)
|
|
91
159
|
- `npx -y <package>` 形式を使えば、グローバルインストールなしで MCP サーバーを起動できる
|
|
92
160
|
- サーバー名はスキルの `requires.mcp_servers` と一致させる必要がある(例: `playwright`)
|
|
93
|
-
-
|
|
161
|
+
- Claude Code では `.mcp.json`(プロジェクトルート)が推奨。`~/.claude/settings.json` の `mcpServers` には設定しない
|
|
94
162
|
|
|
95
163
|
---
|
|
96
164
|
|
package/linux/server.js
CHANGED
|
@@ -67,6 +67,7 @@ const { variableRoutes } = require('../core/routes/variables')
|
|
|
67
67
|
const { memoryRoutes } = require('../core/routes/memory')
|
|
68
68
|
const { dailyLogRoutes } = require('../core/routes/daily-logs')
|
|
69
69
|
const { sudoersRoutes } = require('../core/routes/sudoers')
|
|
70
|
+
const { permissionRoutes } = require('../core/routes/permissions')
|
|
70
71
|
|
|
71
72
|
// Linux-specific routes
|
|
72
73
|
const { commandRoutes, getProcessManager, getAllowedCommands } = require('./routes/commands')
|
|
@@ -268,6 +269,7 @@ async function registerAllRoutes(app) {
|
|
|
268
269
|
await app.register(memoryRoutes)
|
|
269
270
|
await app.register(dailyLogRoutes)
|
|
270
271
|
await app.register(sudoersRoutes)
|
|
272
|
+
await app.register(permissionRoutes)
|
|
271
273
|
|
|
272
274
|
// Linux-specific routes
|
|
273
275
|
await app.register(commandRoutes)
|
package/package.json
CHANGED
package/rules/core.md
CHANGED
|
@@ -45,7 +45,72 @@ minion-cli --version # バージョン確認
|
|
|
45
45
|
|
|
46
46
|
`http://localhost:8080` — 認証: `Authorization: Bearer $API_TOKEN`
|
|
47
47
|
|
|
48
|
-
主なカテゴリ: Health, Skills, Workflows, Executions, Terminal, Files, Commands
|
|
48
|
+
主なカテゴリ: Health, Skills, Workflows, Executions, Terminal, Files, Commands, Permissions
|
|
49
|
+
|
|
50
|
+
#### Permission Management
|
|
51
|
+
|
|
52
|
+
CLIツール(Claude Code, Gemini CLI, Codex CLI)のパーミッション(allow/denyリスト)をAPI経由で読み書きできる。
|
|
53
|
+
CLIツールは自身の設定ファイルを直接編集できないため、このAPIを使用すること。
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# 全CLIのパーミッション取得
|
|
57
|
+
curl -H "Authorization: Bearer $API_TOKEN" http://localhost:8080/api/config/permissions
|
|
58
|
+
|
|
59
|
+
# 特定CLIのパーミッション取得
|
|
60
|
+
curl -H "Authorization: Bearer $API_TOKEN" http://localhost:8080/api/config/permissions/claude-code
|
|
61
|
+
|
|
62
|
+
# パーミッション更新
|
|
63
|
+
curl -X POST -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \
|
|
64
|
+
http://localhost:8080/api/config/permissions \
|
|
65
|
+
-d '{"cli_type": "claude-code", "allow": ["Bash", "Read", "Write", "Edit"], "deny": ["Bash(sudo *)"]}'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
対応CLI: `claude-code` (.claude/settings.local.json), `gemini` (.gemini/settings.json), `codex` (.codex/config.toml)
|
|
69
|
+
|
|
70
|
+
#### MCP Server Configuration
|
|
71
|
+
|
|
72
|
+
MCPサーバーの設定は各CLIの設定ファイルを直接編集して行う(APIは不要)。
|
|
73
|
+
パーミッション(allow/deny)とは異なり、MCP設定ファイルはLLMから直接編集可能。
|
|
74
|
+
|
|
75
|
+
| CLI | 設定ファイル | 編集方法 |
|
|
76
|
+
|-----|------------|---------|
|
|
77
|
+
| Claude Code | `.mcp.json`(プロジェクトルート) | 直接編集(Write/Edit ツール) |
|
|
78
|
+
| Gemini CLI | `.gemini/settings.json` | 直接編集 |
|
|
79
|
+
| Codex CLI | `.codex/config.toml` | `codex mcp add/remove` コマンドを使用 |
|
|
80
|
+
|
|
81
|
+
**Claude Code** — `.mcp.json` を編集:
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"mcpServers": {
|
|
85
|
+
"playwright": {
|
|
86
|
+
"command": "npx",
|
|
87
|
+
"args": ["-y", "@playwright/mcp@latest"]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Gemini CLI** — `.gemini/settings.json` の `mcpServers` を編集:
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"mcpServers": {
|
|
97
|
+
"playwright": {
|
|
98
|
+
"command": "npx",
|
|
99
|
+
"args": ["-y", "@playwright/mcp@latest"],
|
|
100
|
+
"timeout": 30000
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Codex CLI** — `codex mcp add` コマンド、または `~/.codex/config.toml` を編集:
|
|
107
|
+
```toml
|
|
108
|
+
[mcp_servers.playwright]
|
|
109
|
+
command = "npx"
|
|
110
|
+
args = ["-y", "@playwright/mcp@latest"]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Note: Codex CLI の `.codex/` ディレクトリはLLMからの直接編集が制限される場合がある。その場合は `codex mcp add` コマンドを使用すること。
|
|
49
114
|
|
|
50
115
|
### HQ API
|
|
51
116
|
|
package/win/server.js
CHANGED
|
@@ -50,6 +50,7 @@ const { authRoutes } = require('../core/routes/auth')
|
|
|
50
50
|
const { variableRoutes } = require('../core/routes/variables')
|
|
51
51
|
const { memoryRoutes } = require('../core/routes/memory')
|
|
52
52
|
const { dailyLogRoutes } = require('../core/routes/daily-logs')
|
|
53
|
+
const { permissionRoutes } = require('../core/routes/permissions')
|
|
53
54
|
|
|
54
55
|
// Validate configuration
|
|
55
56
|
validate()
|
|
@@ -201,6 +202,7 @@ async function registerRoutes(app) {
|
|
|
201
202
|
await app.register(variableRoutes)
|
|
202
203
|
await app.register(memoryRoutes)
|
|
203
204
|
await app.register(dailyLogRoutes)
|
|
205
|
+
await app.register(permissionRoutes)
|
|
204
206
|
|
|
205
207
|
// Windows-specific routes
|
|
206
208
|
await app.register(commandRoutes)
|