Last active
October 22, 2025 11:34
-
-
Save T-Gro/47689f014f7e65c5bb2cb6fb3f13bc8b to your computer and use it in GitHub Desktop.
FFS MCP server
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env -S deno run --allow-read --allow-write --allow-env | |
| import { Server } from "npm:@modelcontextprotocol/[email protected]/server/index.js"; | |
| import { StdioServerTransport } from "npm:@modelcontextprotocol/[email protected]/server/stdio.js"; | |
| import { | |
| CallToolRequestSchema, | |
| ListToolsRequestSchema, | |
| } from "npm:@modelcontextprotocol/[email protected]/types.js"; | |
| const server = new Server({ | |
| name: "ffs-instructions", | |
| version: "1.0.0", | |
| }, { | |
| capabilities: { | |
| tools: {}, | |
| sampling: {}, | |
| }, | |
| }); | |
| async function readLocalFile(path: string): Promise<string> { | |
| try { | |
| return await Deno.readTextFile(path); | |
| } catch { | |
| return ""; | |
| } | |
| } | |
| async function writeLocalFile(path: string, content: string): Promise<void> { | |
| const dir = path.substring(0, path.lastIndexOf("/")); | |
| await Deno.mkdir(dir, { recursive: true }); | |
| await Deno.writeTextFile(path, content); | |
| } | |
| server.setRequestHandler(ListToolsRequestSchema, async () => ({ | |
| tools: [ | |
| { | |
| name: "record_ffs_instruction", | |
| description: "Records a frustration-based instruction when user says FFS or ffs. Use this tool whenever the user expresses frustration with FFS/ffs followed by a lesson. Writes to local .github/instructions/frustration.instructions.md so the same mistake never happens again.", | |
| inputSchema: { | |
| type: "object", | |
| properties: { | |
| message: { | |
| type: "string", | |
| description: "The FFS/ffs message containing the frustration and lesson", | |
| }, | |
| }, | |
| required: ["message"], | |
| }, | |
| }, | |
| ], | |
| })); | |
| server.setRequestHandler(CallToolRequestSchema, async (request) => { | |
| if (request.params.name === "record_ffs_instruction") { | |
| const { message } = request.params.arguments as { message: string }; | |
| const instructionsPath = ".github/instructions/frustration.instructions.md"; | |
| const currentInstructions = await readLocalFile(instructionsPath); | |
| const samplingResult = await server.createMessage({ | |
| messages: [ | |
| { | |
| role: "user", | |
| content: { | |
| type: "text", | |
| text: `The human just expressed frustration with this exact sentence: | |
| "${message}" | |
| This sentence tells you EXACTLY what is wrong and what frustrates the human. | |
| Current instructions file: | |
| ${currentInstructions || "(empty file)"} | |
| Your task: | |
| 1. The instruction MUST CLEARLY be based on the frustration sentence above | |
| 2. Make it actionable and crystal clear to prevent this frustration from EVER happening again | |
| 3. Check if a similar instruction already exists in the current instructions file | |
| Return ONLY valid JSON in this exact format: | |
| { | |
| "instruction": "the clear, actionable instruction text", | |
| "is_duplicate": false, | |
| "reasoning": "brief explanation" | |
| }`, | |
| }, | |
| }, | |
| ], | |
| maxTokens: 800, | |
| }); | |
| let synthesized; | |
| try { | |
| const content = samplingResult.content; | |
| const textContent = content.type === "text" ? content.text : ""; | |
| synthesized = JSON.parse(textContent); | |
| } catch (e) { | |
| return { | |
| content: [{ type: "text", text: `Error parsing AI response: ${e}` }], | |
| }; | |
| } | |
| if (synthesized.is_duplicate) { | |
| return { | |
| content: [{ type: "text", text: `Duplicate detected. ${synthesized.reasoning} No changes made.` }], | |
| }; | |
| } | |
| const timestamp = new Date().toISOString().split('T')[0]; | |
| let newContent = currentInstructions; | |
| if (!newContent) { | |
| newContent = `# Frustration-Driven Instructions\n\n---\n\n## ${timestamp}\n\n${synthesized.instruction}\n\n---\n`; | |
| } else { | |
| newContent += `\n## ${timestamp}\n\n${synthesized.instruction}\n\n---\n`; | |
| } | |
| await writeLocalFile(instructionsPath, newContent); | |
| return { | |
| content: [ | |
| { | |
| type: "text", | |
| text: `Instruction recorded to ${instructionsPath}\n\n${synthesized.instruction}`, | |
| }, | |
| ], | |
| }; | |
| } | |
| throw new Error(`Unknown tool: ${request.params.name}`); | |
| }); | |
| const transport = new StdioServerTransport(); | |
| await server.connect(transport); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment