faf-mcp 2.2.1 → 2.3.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/CHANGELOG.md +17 -1
- package/CLAUDE.md +3 -3
- package/README.md +8 -3
- package/dist/src/handlers/engine-adapter.js +0 -26
- package/dist/src/handlers/engine-adapter.js.map +1 -1
- package/dist/src/handlers/tools.d.ts +41 -4
- package/dist/src/handlers/tools.js +541 -634
- package/dist/src/handlers/tools.js.map +1 -1
- package/package.json +1 -1
- package/project.faf +2 -2
|
@@ -57,6 +57,28 @@ const path_resolver_1 = require("../utils/path-resolver");
|
|
|
57
57
|
// rationale and the linked tracked issue.
|
|
58
58
|
const faf_cli_bridge_js_1 = require("../utils/faf-cli-bridge.js");
|
|
59
59
|
const turbocat_bridge_js_1 = require("../faf-core/extract/turbocat-bridge.js");
|
|
60
|
+
/**
|
|
61
|
+
* The Core tier — the 15 distinct, well-described tools advertised by default.
|
|
62
|
+
* Everything else is Extended: still callable by name (the dispatch in callTool
|
|
63
|
+
* is unchanged), but advertised only when FAF_TOOLS=all (or FAF_EXTENDED=1).
|
|
64
|
+
* Glama (and any client) runs the server and scores the default tools/list, so a
|
|
65
|
+
* tight, non-overlapping, craft-grade Core is what earns the quality grade —
|
|
66
|
+
* the 40%-MIN rule means one weak tool on the surface caps the whole server.
|
|
67
|
+
*
|
|
68
|
+
* Spine mirrors claude-faf-mcp's proven-A Core; the interop quad
|
|
69
|
+
* (faf_bi_sync + faf_agents/faf_cursor/faf_gemini) is this edition's identity —
|
|
70
|
+
* the Cursor / IDE multi-format sync — where CFM carries memory tools instead.
|
|
71
|
+
* See PLANET-FAF/strategy/claude-faf-mcp-core-tier-glama-a-2026-06-17.md.
|
|
72
|
+
*/
|
|
73
|
+
const CORE_TOOLS = new Set([
|
|
74
|
+
// Spine — mirrors CFM
|
|
75
|
+
'faf_init', 'faf_auto', 'faf_go', 'faf_score', 'faf_doctor',
|
|
76
|
+
'faf_sync', 'faf_context', 'faf_trust', 'faf_about',
|
|
77
|
+
// IDE-edition interop — the differentiator
|
|
78
|
+
'faf_bi_sync', 'faf_agents', 'faf_cursor', 'faf_gemini',
|
|
79
|
+
// Craft-grade utility
|
|
80
|
+
'faf_check', 'faf_git',
|
|
81
|
+
]);
|
|
60
82
|
class FafToolHandler {
|
|
61
83
|
engineAdapter;
|
|
62
84
|
constructor(engineAdapter) {
|
|
@@ -85,609 +107,572 @@ class FafToolHandler {
|
|
|
85
107
|
return this.engineAdapter.getWorkingDirectory();
|
|
86
108
|
}
|
|
87
109
|
async listTools() {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
openWorldHint: false
|
|
97
|
-
},
|
|
98
|
-
inputSchema: {
|
|
99
|
-
type: 'object',
|
|
100
|
-
properties: {},
|
|
101
|
-
additionalProperties: false
|
|
102
|
-
}
|
|
110
|
+
const allTools = [
|
|
111
|
+
{
|
|
112
|
+
name: 'faf_about',
|
|
113
|
+
description: 'Explain what the .faf format is — the IANA-registered, portable context file (application/vnd.faf+yaml) that gives any AI instant project understanding. Returns a plain-language overview of the format and its purpose. Use this when you or the user are new to FAF and want the concept before running other tools.',
|
|
114
|
+
annotations: {
|
|
115
|
+
title: 'About FAF',
|
|
116
|
+
readOnlyHint: true,
|
|
117
|
+
openWorldHint: false
|
|
103
118
|
},
|
|
104
|
-
{
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
inputSchema: {
|
|
120
|
+
type: 'object',
|
|
121
|
+
properties: {},
|
|
122
|
+
additionalProperties: false
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'faf_what',
|
|
127
|
+
description: 'What is .faf format? Quick explanation of project DNA for AI',
|
|
128
|
+
annotations: {
|
|
129
|
+
title: 'What is FAF',
|
|
130
|
+
readOnlyHint: true,
|
|
131
|
+
openWorldHint: false
|
|
117
132
|
},
|
|
118
|
-
{
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
additionalProperties: false
|
|
132
|
-
}
|
|
133
|
+
inputSchema: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {},
|
|
136
|
+
additionalProperties: false
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: 'faf_status',
|
|
141
|
+
description: 'Check if your project has project.faf (project DNA for AI) - Shows AI-readability status',
|
|
142
|
+
annotations: {
|
|
143
|
+
title: 'Project Status',
|
|
144
|
+
readOnlyHint: true,
|
|
145
|
+
openWorldHint: false
|
|
133
146
|
},
|
|
134
|
-
{
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
title: 'AI-Readiness Score',
|
|
139
|
-
readOnlyHint: true,
|
|
140
|
-
openWorldHint: false
|
|
147
|
+
inputSchema: {
|
|
148
|
+
type: 'object',
|
|
149
|
+
properties: {
|
|
150
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
141
151
|
},
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
additionalProperties: false
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'faf_score',
|
|
157
|
+
description: 'Calculate a project.faf AI-readiness score (0-100%) from the populated context slots. Returns the percentage and tier, and with details a slot-by-slot breakdown with improvement suggestions. Use this to measure how complete the AI context is and what to fill next.',
|
|
158
|
+
annotations: {
|
|
159
|
+
title: 'AI-Readiness Score',
|
|
160
|
+
readOnlyHint: true,
|
|
161
|
+
openWorldHint: false
|
|
150
162
|
},
|
|
151
|
-
{
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
readOnlyHint: false,
|
|
157
|
-
destructiveHint: false,
|
|
158
|
-
openWorldHint: false
|
|
163
|
+
inputSchema: {
|
|
164
|
+
type: 'object',
|
|
165
|
+
properties: {
|
|
166
|
+
details: { type: 'boolean', description: 'Include detailed breakdown and improvement suggestions' },
|
|
167
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
159
168
|
},
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
additionalProperties: false
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: 'faf_init',
|
|
174
|
+
description: 'Create a project.faf for a project, making it instantly AI-readable. Returns the new file location and starting score. Accepts a path or bare project name (e.g. ~/Projects/my-app, my-app) and omitting it uses the current directory.',
|
|
175
|
+
annotations: {
|
|
176
|
+
title: 'Initialize .faf',
|
|
177
|
+
readOnlyHint: false,
|
|
178
|
+
destructiveHint: false,
|
|
179
|
+
openWorldHint: false
|
|
171
180
|
},
|
|
172
|
-
{
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
inputSchema: {
|
|
182
|
+
type: 'object',
|
|
183
|
+
properties: {
|
|
184
|
+
path: {
|
|
185
|
+
type: 'string',
|
|
186
|
+
description: 'Project path or name. Smart resolution: "my-app" finds ~/Projects/my-app OR ~/Code/my-app. Full paths like ~/Projects/app or /Users/me/code/app work too. Omit to use current directory.'
|
|
187
|
+
},
|
|
188
|
+
force: { type: 'boolean', description: 'Overwrite existing project.faf if it exists' }
|
|
179
189
|
},
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
190
|
+
additionalProperties: false
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: 'faf_trust',
|
|
195
|
+
description: 'Validate a project.faf for structural integrity and field consistency. Returns trust metrics flagging malformed, missing, or contradictory data. Use this to confirm the context is sound before relying on it or syncing it to other formats.',
|
|
196
|
+
annotations: {
|
|
197
|
+
title: 'Trust Score',
|
|
198
|
+
readOnlyHint: true,
|
|
199
|
+
openWorldHint: false
|
|
185
200
|
},
|
|
186
|
-
{
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
additionalProperties: false
|
|
201
|
-
}
|
|
201
|
+
inputSchema: {
|
|
202
|
+
type: 'object',
|
|
203
|
+
properties: {},
|
|
204
|
+
additionalProperties: false
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: 'faf_sync',
|
|
209
|
+
description: 'Sync a project.faf with CLAUDE.md so the two stay aligned. Returns what was written. Use this for the single CLAUDE.md target; use faf_bi_sync to fan out to AGENTS.md, .cursorrules, and GEMINI.md as well.',
|
|
210
|
+
annotations: {
|
|
211
|
+
title: 'Sync .faf to CLAUDE.md',
|
|
212
|
+
readOnlyHint: false,
|
|
213
|
+
destructiveHint: false,
|
|
214
|
+
openWorldHint: false
|
|
202
215
|
},
|
|
203
|
-
{
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
title: 'Enhance .faf',
|
|
208
|
-
readOnlyHint: false,
|
|
209
|
-
destructiveHint: false,
|
|
210
|
-
openWorldHint: false
|
|
216
|
+
inputSchema: {
|
|
217
|
+
type: 'object',
|
|
218
|
+
properties: {
|
|
219
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
211
220
|
},
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
additionalProperties: false
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: 'faf_bi_sync',
|
|
226
|
+
description: 'Bi-directionally sync a project.faf with CLAUDE.md and, with the format flags or all, also AGENTS.md, .cursorrules, and GEMINI.md. Returns the formats written and any conflicts. Use this to keep every AI tool context file aligned from the single .faf source.',
|
|
227
|
+
annotations: {
|
|
228
|
+
title: 'Bi-directional Sync',
|
|
229
|
+
readOnlyHint: false,
|
|
230
|
+
destructiveHint: false,
|
|
231
|
+
openWorldHint: false
|
|
223
232
|
},
|
|
224
|
-
{
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
233
|
+
inputSchema: {
|
|
234
|
+
type: 'object',
|
|
235
|
+
properties: {
|
|
236
|
+
auto: { type: 'boolean', description: 'Enable automatic synchronization' },
|
|
237
|
+
watch: { type: 'boolean', description: 'Start real-time file watching for changes' },
|
|
238
|
+
force: { type: 'boolean', description: 'Force overwrite conflicting changes' },
|
|
239
|
+
agents: { type: 'boolean', description: 'Also sync to AGENTS.md (OpenAI/Codex format)' },
|
|
240
|
+
cursor: { type: 'boolean', description: 'Also sync to .cursorrules (Cursor IDE format)' },
|
|
241
|
+
gemini: { type: 'boolean', description: 'Also sync to GEMINI.md (Google Gemini format)' },
|
|
242
|
+
all: { type: 'boolean', description: 'Sync to ALL formats: CLAUDE.md + AGENTS.md + .cursorrules + GEMINI.md' },
|
|
243
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
232
244
|
},
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
},
|
|
245
|
-
additionalProperties: false
|
|
246
|
-
}
|
|
245
|
+
additionalProperties: false
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: 'faf_clear',
|
|
250
|
+
description: 'Clear caches, temporary files, and reset FAF state for a fresh start',
|
|
251
|
+
annotations: {
|
|
252
|
+
title: 'Clear .faf Data',
|
|
253
|
+
readOnlyHint: false,
|
|
254
|
+
destructiveHint: true,
|
|
255
|
+
openWorldHint: false
|
|
247
256
|
},
|
|
248
|
-
{
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
openWorldHint: false
|
|
257
|
+
inputSchema: {
|
|
258
|
+
type: 'object',
|
|
259
|
+
properties: {
|
|
260
|
+
cache: { type: 'boolean', description: 'Clear trust cache only' },
|
|
261
|
+
todos: { type: 'boolean', description: 'Clear todo lists only' },
|
|
262
|
+
backups: { type: 'boolean', description: 'Clear backup files only' },
|
|
263
|
+
all: { type: 'boolean', description: 'Clear everything (default)' }
|
|
256
264
|
},
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
additionalProperties: false
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
name: 'faf_debug',
|
|
270
|
+
description: 'Debug faf-mcp environment - show working directory, permissions, and FAF CLI status',
|
|
271
|
+
annotations: {
|
|
272
|
+
title: 'Debug Info',
|
|
273
|
+
readOnlyHint: true,
|
|
274
|
+
openWorldHint: false
|
|
267
275
|
},
|
|
268
|
-
{
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
276
|
+
inputSchema: {
|
|
277
|
+
type: 'object',
|
|
278
|
+
properties: {},
|
|
279
|
+
additionalProperties: false
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: 'faf_read',
|
|
284
|
+
description: 'Read a file within the project root (cwd / FAF_ALLOWED_ROOTS). Paths that escape the project are refused.',
|
|
285
|
+
annotations: {
|
|
286
|
+
title: 'Read .faf File',
|
|
287
|
+
readOnlyHint: true,
|
|
288
|
+
openWorldHint: false
|
|
281
289
|
},
|
|
282
|
-
{
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
290
|
+
inputSchema: {
|
|
291
|
+
type: 'object',
|
|
292
|
+
properties: {
|
|
293
|
+
path: {
|
|
294
|
+
type: 'string',
|
|
295
|
+
description: 'Absolute or relative file path to read'
|
|
296
|
+
}
|
|
289
297
|
},
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
298
|
+
required: ['path'],
|
|
299
|
+
additionalProperties: false
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: 'faf_write',
|
|
304
|
+
description: 'Write a file within the project root (cwd / FAF_ALLOWED_ROOTS). Paths that escape the project are refused.',
|
|
305
|
+
annotations: {
|
|
306
|
+
title: 'Write .faf File',
|
|
307
|
+
readOnlyHint: false,
|
|
308
|
+
destructiveHint: false,
|
|
309
|
+
openWorldHint: false
|
|
301
310
|
},
|
|
302
|
-
{
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
destructiveHint: false,
|
|
309
|
-
openWorldHint: false
|
|
310
|
-
},
|
|
311
|
-
inputSchema: {
|
|
312
|
-
type: 'object',
|
|
313
|
-
properties: {
|
|
314
|
-
path: {
|
|
315
|
-
type: 'string',
|
|
316
|
-
description: 'Absolute or relative file path to write'
|
|
317
|
-
},
|
|
318
|
-
content: {
|
|
319
|
-
type: 'string',
|
|
320
|
-
description: 'Content to write to the file'
|
|
321
|
-
}
|
|
311
|
+
inputSchema: {
|
|
312
|
+
type: 'object',
|
|
313
|
+
properties: {
|
|
314
|
+
path: {
|
|
315
|
+
type: 'string',
|
|
316
|
+
description: 'Absolute or relative file path to write'
|
|
322
317
|
},
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
{
|
|
328
|
-
name: 'faf_list',
|
|
329
|
-
description: 'List directories and discover projects with project.faf files - Essential for FAF discovery workflow',
|
|
330
|
-
annotations: {
|
|
331
|
-
title: 'List .faf Files',
|
|
332
|
-
readOnlyHint: true,
|
|
333
|
-
openWorldHint: false
|
|
318
|
+
content: {
|
|
319
|
+
type: 'string',
|
|
320
|
+
description: 'Content to write to the file'
|
|
321
|
+
}
|
|
334
322
|
},
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
},
|
|
347
|
-
depth: {
|
|
348
|
-
type: 'number',
|
|
349
|
-
enum: [1, 2],
|
|
350
|
-
description: 'Directory depth to scan: 1 (immediate children) or 2 (one level deeper). Default: 1'
|
|
351
|
-
},
|
|
352
|
-
showHidden: {
|
|
353
|
-
type: 'boolean',
|
|
354
|
-
description: 'Show hidden files/directories (starting with .). Default: false'
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
required: ['path'],
|
|
358
|
-
additionalProperties: false
|
|
359
|
-
}
|
|
323
|
+
required: ['path', 'content'],
|
|
324
|
+
additionalProperties: false
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
name: 'faf_list',
|
|
329
|
+
description: 'List directories and discover projects with project.faf files - Essential for FAF discovery workflow',
|
|
330
|
+
annotations: {
|
|
331
|
+
title: 'List .faf Files',
|
|
332
|
+
readOnlyHint: true,
|
|
333
|
+
openWorldHint: false
|
|
360
334
|
},
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
annotations: {
|
|
368
|
-
title: 'Fun FAF Facts',
|
|
369
|
-
readOnlyHint: true,
|
|
370
|
-
openWorldHint: false
|
|
371
|
-
},
|
|
372
|
-
inputSchema: {
|
|
373
|
-
type: 'object',
|
|
374
|
-
properties: {
|
|
375
|
-
test: {
|
|
376
|
-
type: 'string',
|
|
377
|
-
description: 'Test fuzzy matching with typos like "raect" or "chr ext"'
|
|
378
|
-
}
|
|
335
|
+
inputSchema: {
|
|
336
|
+
type: 'object',
|
|
337
|
+
properties: {
|
|
338
|
+
path: {
|
|
339
|
+
type: 'string',
|
|
340
|
+
description: 'Directory path to list (e.g., ~/Projects, /Users/username/Projects)'
|
|
379
341
|
},
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
342
|
+
filter: {
|
|
343
|
+
type: 'string',
|
|
344
|
+
enum: ['faf', 'dirs', 'all'],
|
|
345
|
+
description: 'Filter: "faf" (only dirs with project.faf), "dirs" (all directories), "all" (dirs and files). Default: "dirs"'
|
|
346
|
+
},
|
|
347
|
+
depth: {
|
|
348
|
+
type: 'number',
|
|
349
|
+
enum: [1, 2],
|
|
350
|
+
description: 'Directory depth to scan: 1 (immediate children) or 2 (one level deeper). Default: 1'
|
|
351
|
+
},
|
|
352
|
+
showHidden: {
|
|
353
|
+
type: 'boolean',
|
|
354
|
+
description: 'Show hidden files/directories (starting with .). Default: false'
|
|
355
|
+
}
|
|
390
356
|
},
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
357
|
+
required: ['path'],
|
|
358
|
+
additionalProperties: false
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
// faf_chat — DEPRECATED, un-advertised. The host IS the chat — a chat-shim
|
|
362
|
+
// tool is redundant. Dispatch keeps a deprecation stub (below). Fleet sweep —
|
|
363
|
+
{
|
|
364
|
+
name: 'faf_guide',
|
|
365
|
+
description: 'FAF MCP usage guide for your MCP host - Projects convention, path resolution, and UX patterns',
|
|
366
|
+
annotations: {
|
|
367
|
+
title: 'Usage Guide',
|
|
368
|
+
readOnlyHint: true,
|
|
369
|
+
openWorldHint: false
|
|
396
370
|
},
|
|
397
|
-
{
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
412
|
-
},
|
|
413
|
-
additionalProperties: false
|
|
414
|
-
}
|
|
371
|
+
inputSchema: {
|
|
372
|
+
type: 'object',
|
|
373
|
+
properties: {},
|
|
374
|
+
additionalProperties: false
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
name: 'faf_readme',
|
|
379
|
+
description: 'Extract 6 Ws (Who/What/Why/Where/When/How) from README.md into human_context - Smart pattern matching',
|
|
380
|
+
annotations: {
|
|
381
|
+
title: 'Extract from README',
|
|
382
|
+
readOnlyHint: false,
|
|
383
|
+
destructiveHint: false,
|
|
384
|
+
openWorldHint: false
|
|
415
385
|
},
|
|
416
|
-
{
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
destructiveHint: false,
|
|
423
|
-
openWorldHint: false
|
|
386
|
+
inputSchema: {
|
|
387
|
+
type: 'object',
|
|
388
|
+
properties: {
|
|
389
|
+
apply: { type: 'boolean', description: 'Apply extracted content to project.faf (default: preview only)' },
|
|
390
|
+
force: { type: 'boolean', description: 'Overwrite existing human_context values (default: only fill empty slots)' },
|
|
391
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
424
392
|
},
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
required: ['field', 'value'],
|
|
437
|
-
additionalProperties: false
|
|
438
|
-
}
|
|
393
|
+
additionalProperties: false
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: 'faf_human_add',
|
|
398
|
+
description: 'Add a human_context field (who/what/why/where/when/how) - Non-interactive for MCP',
|
|
399
|
+
annotations: {
|
|
400
|
+
title: 'Add Human Context',
|
|
401
|
+
readOnlyHint: false,
|
|
402
|
+
destructiveHint: false,
|
|
403
|
+
openWorldHint: false
|
|
439
404
|
},
|
|
440
|
-
{
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
},
|
|
448
|
-
inputSchema: {
|
|
449
|
-
type: 'object',
|
|
450
|
-
properties: {
|
|
451
|
-
protect: { type: 'boolean', description: 'Lock good/excellent fields from being overwritten' },
|
|
452
|
-
unlock: { type: 'boolean', description: 'Remove all field protections' },
|
|
453
|
-
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
405
|
+
inputSchema: {
|
|
406
|
+
type: 'object',
|
|
407
|
+
properties: {
|
|
408
|
+
field: {
|
|
409
|
+
type: 'string',
|
|
410
|
+
enum: ['who', 'what', 'why', 'where', 'when', 'how'],
|
|
411
|
+
description: 'The 6 W field to set'
|
|
454
412
|
},
|
|
455
|
-
|
|
456
|
-
|
|
413
|
+
value: { type: 'string', description: 'The value to set for the field' },
|
|
414
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
415
|
+
},
|
|
416
|
+
required: ['field', 'value'],
|
|
417
|
+
additionalProperties: false
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
name: 'faf_check',
|
|
422
|
+
description: 'Inspect the human_context fields and rate each empty/generic/good/excellent. Returns the ratings; with protect it locks good/excellent fields from being overwritten, with unlock it releases them. Use this to gauge context quality and guard your best answers.',
|
|
423
|
+
annotations: {
|
|
424
|
+
title: 'Check .faf Health',
|
|
425
|
+
readOnlyHint: false,
|
|
426
|
+
destructiveHint: false,
|
|
427
|
+
openWorldHint: false
|
|
457
428
|
},
|
|
458
|
-
{
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
openWorldHint: false
|
|
429
|
+
inputSchema: {
|
|
430
|
+
type: 'object',
|
|
431
|
+
properties: {
|
|
432
|
+
protect: { type: 'boolean', description: 'Lock good/excellent fields from being overwritten' },
|
|
433
|
+
unlock: { type: 'boolean', description: 'Remove all field protections' },
|
|
434
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
465
435
|
},
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
436
|
+
additionalProperties: false
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: 'faf_context',
|
|
441
|
+
description: 'Set or show the active project path that later faf_ calls resolve against. Returns the current context path. Call this once at the start of a session so the other tools target the right project.',
|
|
442
|
+
annotations: {
|
|
443
|
+
title: 'View Context',
|
|
444
|
+
readOnlyHint: true,
|
|
445
|
+
openWorldHint: false
|
|
473
446
|
},
|
|
474
|
-
{
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
title: 'Guided Setup',
|
|
479
|
-
readOnlyHint: false,
|
|
480
|
-
destructiveHint: false,
|
|
481
|
-
openWorldHint: false
|
|
447
|
+
inputSchema: {
|
|
448
|
+
type: 'object',
|
|
449
|
+
properties: {
|
|
450
|
+
path: { type: 'string', description: 'Set active project path. If omitted, shows current context.' }
|
|
482
451
|
},
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
}
|
|
452
|
+
additionalProperties: false
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
name: 'faf_go',
|
|
457
|
+
description: 'Drive a project.faf to 100% through a guided interview — returns the next questions for the missing human-context and goal fields, then applies the answers passed back. Returns the updated score after each round. Use this to close the gap to a complete context when auto-detection cannot fill the human slots.',
|
|
458
|
+
annotations: {
|
|
459
|
+
title: 'Guided Setup',
|
|
460
|
+
readOnlyHint: false,
|
|
461
|
+
destructiveHint: false,
|
|
462
|
+
openWorldHint: false
|
|
495
463
|
},
|
|
496
|
-
{
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
464
|
+
inputSchema: {
|
|
465
|
+
type: 'object',
|
|
466
|
+
properties: {
|
|
467
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' },
|
|
468
|
+
answers: {
|
|
469
|
+
type: 'object',
|
|
470
|
+
description: 'Answers to apply. Keys are field paths (e.g., "project.goal", "human_context.why"), values are the answers. If provided, applies answers and returns new score.',
|
|
471
|
+
additionalProperties: { type: 'string' }
|
|
472
|
+
}
|
|
504
473
|
},
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
474
|
+
additionalProperties: false
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
name: 'faf_auto',
|
|
479
|
+
description: 'Run the full setup pipeline in one call — init, stack detection, sync, bi-sync, and score — taking a project from no context to a complete project.faf. Returns the final AI-readiness score and what was created. Use this as the fast path on a fresh project; use the individual tools when you need finer control.',
|
|
480
|
+
annotations: {
|
|
481
|
+
title: 'Auto-detect Context',
|
|
482
|
+
readOnlyHint: false,
|
|
483
|
+
destructiveHint: false,
|
|
484
|
+
openWorldHint: false
|
|
513
485
|
},
|
|
514
|
-
{
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
readOnlyHint: true,
|
|
520
|
-
openWorldHint: false
|
|
486
|
+
inputSchema: {
|
|
487
|
+
type: 'object',
|
|
488
|
+
properties: {
|
|
489
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' },
|
|
490
|
+
force: { type: 'boolean', description: 'Force overwrite existing files' }
|
|
521
491
|
},
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
492
|
+
additionalProperties: false
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
name: 'faf_dna',
|
|
497
|
+
description: 'Show your FAF DNA journey - See your evolution from birth to championship (22% 85% 99%)',
|
|
498
|
+
annotations: {
|
|
499
|
+
title: 'View Project DNA',
|
|
500
|
+
readOnlyHint: true,
|
|
501
|
+
openWorldHint: false
|
|
529
502
|
},
|
|
530
|
-
{
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
title: 'List Formats',
|
|
535
|
-
readOnlyHint: true,
|
|
536
|
-
openWorldHint: false
|
|
503
|
+
inputSchema: {
|
|
504
|
+
type: 'object',
|
|
505
|
+
properties: {
|
|
506
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
537
507
|
},
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
508
|
+
additionalProperties: false
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
name: 'faf_formats',
|
|
513
|
+
description: 'Scan the project for tech-stack signals across 150+ manifest types (package.json, Cargo.toml, pyproject.toml, go.mod, and more) and surface which project.faf stack slots to fill. Returns the detected formats and slot-fill recommendations. Use this to populate the technical context.',
|
|
514
|
+
annotations: {
|
|
515
|
+
title: 'List Formats',
|
|
516
|
+
readOnlyHint: true,
|
|
517
|
+
openWorldHint: false
|
|
546
518
|
},
|
|
547
|
-
{
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
readOnlyHint: false,
|
|
553
|
-
destructiveHint: false,
|
|
554
|
-
openWorldHint: false
|
|
519
|
+
inputSchema: {
|
|
520
|
+
type: 'object',
|
|
521
|
+
properties: {
|
|
522
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' },
|
|
523
|
+
json: { type: 'boolean', description: 'Return results as JSON' }
|
|
555
524
|
},
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
525
|
+
additionalProperties: false
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
name: 'faf_quick',
|
|
530
|
+
description: 'Lightning-fast .faf creation - One-liner format: "name, description, language, framework, hosting"',
|
|
531
|
+
annotations: {
|
|
532
|
+
title: 'Quick Create',
|
|
533
|
+
readOnlyHint: false,
|
|
534
|
+
destructiveHint: false,
|
|
535
|
+
openWorldHint: false
|
|
566
536
|
},
|
|
567
|
-
{
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
openWorldHint: false
|
|
537
|
+
inputSchema: {
|
|
538
|
+
type: 'object',
|
|
539
|
+
properties: {
|
|
540
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' },
|
|
541
|
+
input: { type: 'string', description: 'Quick input: "project-name, description, language, framework, hosting" (minimum: name, description)' },
|
|
542
|
+
force: { type: 'boolean', description: 'Force overwrite existing .faf file' }
|
|
574
543
|
},
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
544
|
+
required: ['input'],
|
|
545
|
+
additionalProperties: false
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
name: 'faf_doctor',
|
|
550
|
+
description: 'Diagnose a project.faf setup — report missing files, empty or weak slots, and common configuration issues, each with how to fix it. Returns a prioritized checklist. Use this when your score is low and you need to know why.',
|
|
551
|
+
annotations: {
|
|
552
|
+
title: 'Diagnose Issues',
|
|
553
|
+
readOnlyHint: true,
|
|
554
|
+
openWorldHint: false
|
|
582
555
|
},
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
name: 'faf_agents',
|
|
588
|
-
description: 'Import/Export/Sync between AGENTS.md (OpenAI/Codex) and project.faf - AI interop!',
|
|
589
|
-
annotations: {
|
|
590
|
-
title: 'Sync AGENTS.md',
|
|
591
|
-
readOnlyHint: false,
|
|
592
|
-
destructiveHint: false,
|
|
593
|
-
openWorldHint: false
|
|
556
|
+
inputSchema: {
|
|
557
|
+
type: 'object',
|
|
558
|
+
properties: {
|
|
559
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
594
560
|
},
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
561
|
+
additionalProperties: false
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
// ============================================================================
|
|
565
|
+
// v4.5.0 INTEROP TOOLS
|
|
566
|
+
// ============================================================================
|
|
567
|
+
{
|
|
568
|
+
name: 'faf_agents',
|
|
569
|
+
description: 'Import, export, or sync context between AGENTS.md (the OpenAI/Codex convention) and project.faf. Returns the merged or written result for the chosen action. Use this to keep a Codex/OpenAI-style AGENTS.md and your .faf in agreement from one source.',
|
|
570
|
+
annotations: {
|
|
571
|
+
title: 'Sync AGENTS.md',
|
|
572
|
+
readOnlyHint: false,
|
|
573
|
+
destructiveHint: false,
|
|
574
|
+
openWorldHint: false
|
|
606
575
|
},
|
|
607
|
-
{
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
openWorldHint: false
|
|
576
|
+
inputSchema: {
|
|
577
|
+
type: 'object',
|
|
578
|
+
properties: {
|
|
579
|
+
action: { type: 'string', enum: ['import', 'export', 'sync'], description: 'Action: import (AGENTS.md -> .faf), export (.faf -> AGENTS.md), sync (bidirectional)' },
|
|
580
|
+
force: { type: 'boolean', description: 'Force overwrite existing files' },
|
|
581
|
+
merge: { type: 'boolean', description: 'Merge imported data with existing .faf instead of replacing' },
|
|
582
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
615
583
|
},
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
584
|
+
required: ['action'],
|
|
585
|
+
additionalProperties: false
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
name: 'faf_cursor',
|
|
590
|
+
description: 'Import, export, or sync context between .cursorrules (the Cursor IDE convention) and project.faf. Returns the merged or written result for the chosen action. Use this to keep Cursor rules and your .faf in agreement from one source.',
|
|
591
|
+
annotations: {
|
|
592
|
+
title: 'Sync .cursorrules',
|
|
593
|
+
readOnlyHint: false,
|
|
594
|
+
destructiveHint: false,
|
|
595
|
+
openWorldHint: false
|
|
627
596
|
},
|
|
628
|
-
{
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
openWorldHint: false
|
|
597
|
+
inputSchema: {
|
|
598
|
+
type: 'object',
|
|
599
|
+
properties: {
|
|
600
|
+
action: { type: 'string', enum: ['import', 'export', 'sync'], description: 'Action: import (.cursorrules -> .faf), export (.faf -> .cursorrules), sync (bidirectional)' },
|
|
601
|
+
force: { type: 'boolean', description: 'Force overwrite existing files' },
|
|
602
|
+
merge: { type: 'boolean', description: 'Merge imported data with existing .faf instead of replacing' },
|
|
603
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
636
604
|
},
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
605
|
+
required: ['action'],
|
|
606
|
+
additionalProperties: false
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
name: 'faf_gemini',
|
|
611
|
+
description: 'Import, export, or sync context between GEMINI.md (the Google Gemini CLI convention) and project.faf. Returns the merged or written result for the chosen action. Use this to keep a Gemini context file and your .faf in agreement from one source.',
|
|
612
|
+
annotations: {
|
|
613
|
+
title: 'Sync GEMINI.md',
|
|
614
|
+
readOnlyHint: false,
|
|
615
|
+
destructiveHint: false,
|
|
616
|
+
openWorldHint: false
|
|
648
617
|
},
|
|
649
|
-
{
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
openWorldHint: false
|
|
618
|
+
inputSchema: {
|
|
619
|
+
type: 'object',
|
|
620
|
+
properties: {
|
|
621
|
+
action: { type: 'string', enum: ['import', 'export', 'sync'], description: 'Action: import (GEMINI.md -> .faf), export (.faf -> GEMINI.md), sync (bidirectional)' },
|
|
622
|
+
force: { type: 'boolean', description: 'Force overwrite existing files' },
|
|
623
|
+
merge: { type: 'boolean', description: 'Merge imported data with existing .faf instead of replacing' },
|
|
624
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
657
625
|
},
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
626
|
+
required: ['action'],
|
|
627
|
+
additionalProperties: false
|
|
628
|
+
}
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
name: 'faf_conductor',
|
|
632
|
+
description: 'Import/Export between conductor/ directory (Google Conductor) and project.faf - AI interop!',
|
|
633
|
+
annotations: {
|
|
634
|
+
title: 'Sync Conductor',
|
|
635
|
+
readOnlyHint: false,
|
|
636
|
+
destructiveHint: false,
|
|
637
|
+
openWorldHint: false
|
|
669
638
|
},
|
|
670
|
-
{
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
openWorldHint: false
|
|
639
|
+
inputSchema: {
|
|
640
|
+
type: 'object',
|
|
641
|
+
properties: {
|
|
642
|
+
action: { type: 'string', enum: ['import', 'export'], description: 'Action: import (conductor/ -> .faf), export (.faf -> conductor/)' },
|
|
643
|
+
force: { type: 'boolean', description: 'Force overwrite existing files' },
|
|
644
|
+
merge: { type: 'boolean', description: 'Merge imported data with existing .faf instead of replacing' },
|
|
645
|
+
path: { type: 'string', description: 'Project path. Sets session context for subsequent calls.' }
|
|
678
646
|
},
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
647
|
+
required: ['action'],
|
|
648
|
+
additionalProperties: false
|
|
649
|
+
}
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
name: 'faf_git',
|
|
653
|
+
description: 'Generate a project.faf from any public GitHub repository URL — fetches the repo and extracts its stack and context. Returns the generated .faf. Use this to bootstrap context for a project you have the URL for but not cloned locally.',
|
|
654
|
+
annotations: {
|
|
655
|
+
title: 'Extract from GitHub',
|
|
656
|
+
readOnlyHint: false,
|
|
657
|
+
destructiveHint: false,
|
|
658
|
+
openWorldHint: true
|
|
688
659
|
},
|
|
689
|
-
|
|
690
|
-
|
|
660
|
+
inputSchema: {
|
|
661
|
+
type: 'object',
|
|
662
|
+
properties: {
|
|
663
|
+
url: { type: 'string', description: 'GitHub repository URL (e.g., https://github.com/owner/repo or owner/repo)' },
|
|
664
|
+
path: { type: 'string', description: 'Output directory for generated project.faf. If omitted, returns content without writing.' }
|
|
665
|
+
},
|
|
666
|
+
required: ['url'],
|
|
667
|
+
additionalProperties: false
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
];
|
|
671
|
+
// Core-tier gate: advertise only the Core by default; FAF_TOOLS=all (or
|
|
672
|
+
// FAF_EXTENDED=1) exposes the full set. Dispatch in callTool keeps every
|
|
673
|
+
// tool callable by name regardless — the gate only shapes tools/list.
|
|
674
|
+
const showAll = process.env.FAF_TOOLS === 'all' || process.env.FAF_EXTENDED === '1';
|
|
675
|
+
return { tools: showAll ? allTools : allTools.filter((t) => CORE_TOOLS.has(t.name)) };
|
|
691
676
|
}
|
|
692
677
|
async callTool(name, args) {
|
|
693
678
|
// Input validation
|
|
@@ -706,8 +691,6 @@ class FafToolHandler {
|
|
|
706
691
|
return await this.handleFafTrust(args);
|
|
707
692
|
case 'faf_sync':
|
|
708
693
|
return await this.handleFafSync(args);
|
|
709
|
-
case 'faf_enhance':
|
|
710
|
-
return await this.handleFafEnhance(args);
|
|
711
694
|
case 'faf_bi_sync':
|
|
712
695
|
return await this.handleFafBiSync(args);
|
|
713
696
|
case 'faf_clear':
|
|
@@ -729,8 +712,6 @@ class FafToolHandler {
|
|
|
729
712
|
}
|
|
730
713
|
case 'faf_chat':
|
|
731
714
|
return await this.handleFafChat(args);
|
|
732
|
-
case 'faf_friday':
|
|
733
|
-
return await this.handleFafFriday(args);
|
|
734
715
|
case 'faf_write':
|
|
735
716
|
return await fileHandler_1.fileHandlers.faf_write(args);
|
|
736
717
|
case 'faf_list':
|
|
@@ -1056,44 +1037,6 @@ package_manager: ${projectData.package_manager}` : ''}
|
|
|
1056
1037
|
}]
|
|
1057
1038
|
};
|
|
1058
1039
|
}
|
|
1059
|
-
async handleFafEnhance(args) {
|
|
1060
|
-
// Set project context if path provided
|
|
1061
|
-
if (args?.path) {
|
|
1062
|
-
this.getProjectPath(args.path);
|
|
1063
|
-
}
|
|
1064
|
-
const enhanceArgs = [];
|
|
1065
|
-
// Default to Claude optimization if no model specified
|
|
1066
|
-
const model = args?.model || 'claude';
|
|
1067
|
-
enhanceArgs.push('--model', model);
|
|
1068
|
-
if (args?.focus) {
|
|
1069
|
-
enhanceArgs.push('--focus', args.focus);
|
|
1070
|
-
}
|
|
1071
|
-
if (args?.consensus) {
|
|
1072
|
-
enhanceArgs.push('--consensus');
|
|
1073
|
-
}
|
|
1074
|
-
if (args?.dryRun) {
|
|
1075
|
-
enhanceArgs.push('--dry-run');
|
|
1076
|
-
}
|
|
1077
|
-
const result = await this.engineAdapter.callEngine('enhance', enhanceArgs);
|
|
1078
|
-
if (!result.success) {
|
|
1079
|
-
return {
|
|
1080
|
-
content: [{
|
|
1081
|
-
type: 'text',
|
|
1082
|
-
text: `🚀 Claude FAF Enhancement:\n\nFailed to enhance: ${result.error}`
|
|
1083
|
-
}],
|
|
1084
|
-
isError: true
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
const output = typeof result.data === 'string'
|
|
1088
|
-
? result.data
|
|
1089
|
-
: result.data?.message || result.data?.output || JSON.stringify(result.data, null, 2);
|
|
1090
|
-
return {
|
|
1091
|
-
content: [{
|
|
1092
|
-
type: 'text',
|
|
1093
|
-
text: `🚀 Claude FAF Enhancement:\n\n${output}`
|
|
1094
|
-
}]
|
|
1095
|
-
};
|
|
1096
|
-
}
|
|
1097
1040
|
async handleFafBiSync(args) {
|
|
1098
1041
|
// Set project context if path provided
|
|
1099
1042
|
if (args?.path) {
|
|
@@ -1168,12 +1111,17 @@ package_manager: ${projectData.package_manager}` : ''}
|
|
|
1168
1111
|
const packageInfo = {
|
|
1169
1112
|
name: 'faf-mcp',
|
|
1170
1113
|
version: version_1.VERSION,
|
|
1171
|
-
description: 'We ARE the C in MCP. I
|
|
1114
|
+
description: 'We ARE the C in MCP. I - The formula that changes everything.',
|
|
1172
1115
|
author: 'FAF Team (team@faf.one)',
|
|
1173
1116
|
website: 'https://faf.one',
|
|
1174
1117
|
npm: 'https://www.npmjs.com/package/faf-mcp'
|
|
1175
1118
|
};
|
|
1176
|
-
const aboutText =
|
|
1119
|
+
const aboutText = `well seeing as you clearly have time to burn, faf is about saving your AI time, how is that working out...
|
|
1120
|
+
|
|
1121
|
+
faff about (v., Brit.) — what your AI does for 20 minutes relearning your project, every session.
|
|
1122
|
+
.faf (n.) — what stops it. Same four letters, opposite outcomes.
|
|
1123
|
+
|
|
1124
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1177
1125
|
🤖 .faf = project DNA for AI
|
|
1178
1126
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1179
1127
|
|
|
@@ -1318,47 +1266,6 @@ ${debugInfo.permissions.fafError ? ` FAF Error: ${debugInfo.permissions.fafErr
|
|
|
1318
1266
|
}],
|
|
1319
1267
|
};
|
|
1320
1268
|
}
|
|
1321
|
-
async handleFafFriday(args) {
|
|
1322
|
-
const { test } = args || {};
|
|
1323
|
-
let response = `🎉 **Friday Features in FAF MCP!**\n\n`;
|
|
1324
|
-
response += `**Chrome Extension Auto-Detection** | Boosts scores to 90%+ automatically\n`;
|
|
1325
|
-
response += `**Universal Fuzzy Matching** | Typo-tolerant: "raect"→"react", "chr ext"→"chrome extension"\n`;
|
|
1326
|
-
response += `**Intel-Friday™** | Smart IF statements that add massive value\n\n`;
|
|
1327
|
-
if (test) {
|
|
1328
|
-
// Test fuzzy matching
|
|
1329
|
-
const suggestion = fuzzy_detector_1.FuzzyDetector.getSuggestion(test);
|
|
1330
|
-
const projectType = fuzzy_detector_1.FuzzyDetector.detectProjectType(test);
|
|
1331
|
-
const chromeDetection = fuzzy_detector_1.FuzzyDetector.detectChromeExtension(test);
|
|
1332
|
-
response += `\n**Testing: "${test}"**\n`;
|
|
1333
|
-
if (suggestion) {
|
|
1334
|
-
response += `✅ Fuzzy Match: "${test}" → "${suggestion}"\n`;
|
|
1335
|
-
}
|
|
1336
|
-
response += `📦 Project Type Detected: ${projectType}\n`;
|
|
1337
|
-
if (chromeDetection.detected) {
|
|
1338
|
-
response += `🎯 Chrome Extension Detected! (Confidence: ${chromeDetection.confidence})\n`;
|
|
1339
|
-
if (chromeDetection.corrected) {
|
|
1340
|
-
response += ` Corrected from: "${test}" → "${chromeDetection.corrected}"\n`;
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
// Show what would be auto-filled
|
|
1344
|
-
if (chromeDetection.detected && chromeDetection.confidence === 'high') {
|
|
1345
|
-
response += `\n**Auto-fill Preview (7 slots for 90%+ score):**\n`;
|
|
1346
|
-
const slots = fuzzy_detector_1.FuzzyDetector.getChromeExtensionSlots();
|
|
1347
|
-
for (const [key, value] of Object.entries(slots)) {
|
|
1348
|
-
response += `• ${key}: ${value}\n`;
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
else {
|
|
1353
|
-
response += `\n💡 Try: \`faf_friday test:"raect"\` or \`faf_friday test:"chr ext"\``;
|
|
1354
|
-
}
|
|
1355
|
-
return {
|
|
1356
|
-
content: [{
|
|
1357
|
-
type: 'text',
|
|
1358
|
-
text: response
|
|
1359
|
-
}]
|
|
1360
|
-
};
|
|
1361
|
-
}
|
|
1362
1269
|
async handleFafGuide(_args) {
|
|
1363
1270
|
const guide = `# FAF MCP - your MCP host Guide
|
|
1364
1271
|
|