@gesslar/fluffos-mcp 0.1.3 → 0.1.5
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/.github/workflows/Quality.yaml +21 -0
- package/.github/workflows/Release.yaml +16 -0
- package/eslint.config.js +8 -164
- package/package.json +31 -26
- package/scripts/search_docs.sh +0 -0
- package/.github/workflows/ci.yml +0 -33
- package/index.js +0 -306
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Quality
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
schedule:
|
|
10
|
+
- cron: "20 14 * * 1"
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
Quality:
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
uses: gesslar/Maint/.github/workflows/Quality.yaml@main
|
|
17
|
+
secrets: inherit
|
|
18
|
+
with:
|
|
19
|
+
package_manager: "auto"
|
|
20
|
+
perform_linting: "${{ vars.PERFORM_LINTING || 'yes' }}"
|
|
21
|
+
perform_testing: "${{ vars.PERFORM_TESTING || 'no' }}"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [closed]
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
Release:
|
|
10
|
+
uses: gesslar/Maint/.github/workflows/Release.yaml@main
|
|
11
|
+
secrets: inherit
|
|
12
|
+
with:
|
|
13
|
+
package_manager: "auto"
|
|
14
|
+
quality_check: "Quality"
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write
|
package/eslint.config.js
CHANGED
|
@@ -1,167 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import jsdoc from "eslint-plugin-jsdoc"
|
|
3
|
-
import stylistic from "@stylistic/eslint-plugin"
|
|
4
|
-
import globals from "globals"
|
|
1
|
+
import uglify from "@gesslar/uglier"
|
|
5
2
|
|
|
6
3
|
export default [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
ecmaVersion: "latest",
|
|
15
|
-
sourceType: "module",
|
|
16
|
-
globals: {
|
|
17
|
-
...globals.node,
|
|
18
|
-
fetch: "readonly",
|
|
19
|
-
Headers: "readonly",
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
// Add override for webview files to include browser globals
|
|
24
|
-
{
|
|
25
|
-
name: "gesslar/uglier/webview-env",
|
|
26
|
-
files: ["src/webview/**/*.{js,mjs,cjs}"],
|
|
27
|
-
languageOptions: {
|
|
28
|
-
globals: {
|
|
29
|
-
...globals.browser,
|
|
30
|
-
acquireVsCodeApi: "readonly"
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
// Add override for .cjs files to treat as CommonJS
|
|
35
|
-
{
|
|
36
|
-
name: "gesslar/uglier/cjs-override",
|
|
37
|
-
files: ["src/**/*.cjs"],
|
|
38
|
-
languageOptions: {
|
|
39
|
-
sourceType: "script",
|
|
40
|
-
ecmaVersion: 2021
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
// Add override for .mjs files to treat as ES modules
|
|
44
|
-
{
|
|
45
|
-
name: "gesslar/uglier/mjs-override",
|
|
46
|
-
files: ["src/**/*.mjs"],
|
|
47
|
-
languageOptions: {
|
|
48
|
-
sourceType: "module",
|
|
49
|
-
ecmaVersion: 2021
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
name: "gesslar/uglier/lints-js",
|
|
54
|
-
files: ["{work,src}/**/*.{mjs,cjs,js}"],
|
|
55
|
-
plugins: {
|
|
56
|
-
"@stylistic": stylistic,
|
|
57
|
-
},
|
|
58
|
-
rules: {
|
|
59
|
-
"@stylistic/arrow-parens": ["error", "as-needed"],
|
|
60
|
-
"@stylistic/arrow-spacing": ["error", { before: true, after: true }],
|
|
61
|
-
"@stylistic/brace-style": ["error", "1tbs", {allowSingleLine: false}],
|
|
62
|
-
"@stylistic/nonblock-statement-body-position": ["error", "below"],
|
|
63
|
-
"@stylistic/padding-line-between-statements": [
|
|
64
|
-
"error",
|
|
65
|
-
{blankLine: "always", prev: "if", next: "*"},
|
|
66
|
-
{blankLine: "always", prev: "*", next: "return"},
|
|
67
|
-
{blankLine: "always", prev: "while", next: "*"},
|
|
68
|
-
{blankLine: "always", prev: "for", next: "*"},
|
|
69
|
-
{blankLine: "always", prev: "switch", next: "*"},
|
|
70
|
-
{blankLine: "always", prev: "do", next: "*"},
|
|
71
|
-
// {blankLine: "always", prev: ["const", "let", "var"], next: "*"},
|
|
72
|
-
// {blankLine: "any", prev: ["const", "let", "var"], next: ["const", "let", "var"]},
|
|
73
|
-
{blankLine: "always", prev: "directive", next: "*" },
|
|
74
|
-
{blankLine: "any", prev: "directive", next: "directive" },
|
|
75
|
-
],
|
|
76
|
-
"@stylistic/eol-last": ["error", "always"],
|
|
77
|
-
"@stylistic/indent": ["error", 2, {
|
|
78
|
-
SwitchCase: 1 // Indents `case` statements one level deeper than `switch`
|
|
79
|
-
}],
|
|
80
|
-
"@stylistic/key-spacing": ["error", { beforeColon: false, afterColon: true }],
|
|
81
|
-
"@stylistic/keyword-spacing": ["error", {
|
|
82
|
-
before: false,
|
|
83
|
-
after: true,
|
|
84
|
-
overrides: {
|
|
85
|
-
// Control statements
|
|
86
|
-
return: { before: true, after: true },
|
|
87
|
-
if: { after: false },
|
|
88
|
-
else: { before: true, after: true },
|
|
89
|
-
for: { after: false },
|
|
90
|
-
while: { before: true, after: false },
|
|
91
|
-
do: { after: true },
|
|
92
|
-
switch: { after: false },
|
|
93
|
-
case: { before: true, after: true },
|
|
94
|
-
throw: { before: true, after: false } ,
|
|
95
|
-
|
|
96
|
-
// Keywords
|
|
97
|
-
as: { before: true, after: true },
|
|
98
|
-
of: { before: true, after: true },
|
|
99
|
-
from: { before: true, after: true },
|
|
100
|
-
async: { before: true, after: true },
|
|
101
|
-
await: { before: true, after: false },
|
|
102
|
-
class: { before: true, after: true },
|
|
103
|
-
const: { before: true, after: true },
|
|
104
|
-
let: { before: true, after: true },
|
|
105
|
-
var: { before: true, after: true },
|
|
106
|
-
|
|
107
|
-
// Exception handling
|
|
108
|
-
catch: { before: true, after: true },
|
|
109
|
-
finally: { before: true, after: true },
|
|
110
|
-
}
|
|
111
|
-
}],
|
|
112
|
-
// Blocks
|
|
113
|
-
"@stylistic/space-before-blocks": ["error", "always"],
|
|
114
|
-
"@stylistic/max-len": ["warn", {
|
|
115
|
-
code: 80,
|
|
116
|
-
ignoreComments: true,
|
|
117
|
-
ignoreUrls: true,
|
|
118
|
-
ignoreStrings: true,
|
|
119
|
-
ignoreTemplateLiterals: true,
|
|
120
|
-
ignoreRegExpLiterals: true,
|
|
121
|
-
tabWidth: 2
|
|
122
|
-
}],
|
|
123
|
-
"@stylistic/no-tabs": "error",
|
|
124
|
-
"@stylistic/no-trailing-spaces": ["error"],
|
|
125
|
-
"@stylistic/object-curly-spacing": ["error", "never", {
|
|
126
|
-
objectsInObjects: false,
|
|
127
|
-
arraysInObjects: false
|
|
128
|
-
}],
|
|
129
|
-
"@stylistic/quotes": ["error", "double", {
|
|
130
|
-
avoidEscape: true,
|
|
131
|
-
allowTemplateLiterals: "always"
|
|
132
|
-
}],
|
|
133
|
-
"@stylistic/semi": ["error", "never"],
|
|
134
|
-
"@stylistic/space-before-function-paren": ["error", "never"],
|
|
135
|
-
"@stylistic/yield-star-spacing": ["error", { before: true, after: false }],
|
|
136
|
-
"constructor-super": "error",
|
|
137
|
-
"no-unexpected-multiline": "error",
|
|
138
|
-
"no-unused-vars": ["error", {
|
|
139
|
-
caughtErrors: "all",
|
|
140
|
-
caughtErrorsIgnorePattern: "^_+",
|
|
141
|
-
argsIgnorePattern: "^_+",
|
|
142
|
-
destructuredArrayIgnorePattern: "^_+",
|
|
143
|
-
varsIgnorePattern: "^_+"
|
|
144
|
-
}],
|
|
145
|
-
"no-useless-assignment": "error",
|
|
146
|
-
"prefer-const": "error",
|
|
147
|
-
"@stylistic/no-multiple-empty-lines": ["error", { max: 1 }],
|
|
148
|
-
"@stylistic/array-bracket-spacing": ["error", "never"],
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
name: "gesslar/uglier/lints-jsdoc",
|
|
153
|
-
files: ["{work,src}/**/*.{mjs,cjs,js}"],
|
|
154
|
-
plugins: {
|
|
155
|
-
jsdoc,
|
|
156
|
-
},
|
|
157
|
-
rules: {
|
|
158
|
-
"jsdoc/require-description": "error",
|
|
159
|
-
"jsdoc/tag-lines": ["error", "any", {"startLines":1}],
|
|
160
|
-
"jsdoc/require-jsdoc": ["error", { publicOnly: true }],
|
|
161
|
-
"jsdoc/check-tag-names": "error",
|
|
162
|
-
"jsdoc/check-types": "error",
|
|
163
|
-
"jsdoc/require-param-type": "error",
|
|
164
|
-
"jsdoc/require-returns-type": "error"
|
|
165
|
-
}
|
|
166
|
-
}
|
|
4
|
+
...uglify({
|
|
5
|
+
with: [
|
|
6
|
+
"lints-js", // default files: ["**/*.{js,mjs,cjs}"]
|
|
7
|
+
"lints-jsdoc", // default files: ["**/*.{js,mjs,cjs}"]
|
|
8
|
+
"node", // default files: ["**/*.{js,mjs,cjs}"]
|
|
9
|
+
]
|
|
10
|
+
})
|
|
167
11
|
]
|
package/package.json
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gesslar/fluffos-mcp",
|
|
3
|
-
"version": "0.1.3",
|
|
4
3
|
"description": "MCP server for FluffOS driver tools - validate and disassemble LPC code",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"fluffos-mcp": "./src/index.js"
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "gesslar",
|
|
6
|
+
"url": "https://gesslar.dev"
|
|
9
7
|
},
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"pr": "gt submit --publish --restack --ai -m",
|
|
17
|
-
"patch": "npm version patch",
|
|
18
|
-
"minor": "npm version minor",
|
|
19
|
-
"major": "npm version major"
|
|
8
|
+
"version": "0.1.5",
|
|
9
|
+
"license": "Unlicense",
|
|
10
|
+
"homepage": "https://github.com/gesslar/fluffos-mcp#readme",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/gesslar/fluffos-mcp.git"
|
|
20
14
|
},
|
|
21
15
|
"keywords": [
|
|
22
16
|
"fluffos",
|
|
@@ -29,19 +23,30 @@
|
|
|
29
23
|
"white",
|
|
30
24
|
"cheddar"
|
|
31
25
|
],
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22"
|
|
28
|
+
},
|
|
29
|
+
"main": "src/index.js",
|
|
30
|
+
"type": "module",
|
|
31
|
+
"bin": {
|
|
32
|
+
"fluffos-mcp": "./src/index.js"
|
|
37
33
|
},
|
|
38
34
|
"dependencies": {
|
|
39
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.25.1"
|
|
40
36
|
},
|
|
41
37
|
"devDependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
"@gesslar/uglier": "^0.5.1",
|
|
39
|
+
"eslint": "^9.39.2"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"start": "node src/index.js",
|
|
43
|
+
"lint": "eslint src/",
|
|
44
|
+
"lint:fix": "eslint src/ --fix",
|
|
45
|
+
"submit": "pnpm publish --access public --//registry.npmjs.org/:_authToken=\"${NPM_ACCESS_TOKEN}\"",
|
|
46
|
+
"update": "pnpm up --latest --recursive",
|
|
47
|
+
"pr": "gt submit -p --ai",
|
|
48
|
+
"patch": "pnpm version patch",
|
|
49
|
+
"minor": "pnpm version minor",
|
|
50
|
+
"major": "pnpm version major"
|
|
46
51
|
}
|
|
47
|
-
}
|
|
52
|
+
}
|
package/scripts/search_docs.sh
CHANGED
|
File without changes
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
name: Giddyup
|
|
2
|
-
permissions:
|
|
3
|
-
contents: read
|
|
4
|
-
|
|
5
|
-
on:
|
|
6
|
-
push:
|
|
7
|
-
branches: [main]
|
|
8
|
-
pull_request:
|
|
9
|
-
branches: [main]
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
cowyboysounds:
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
|
|
15
|
-
strategy:
|
|
16
|
-
matrix:
|
|
17
|
-
node-version: [20.x, 22.x]
|
|
18
|
-
|
|
19
|
-
steps:
|
|
20
|
-
- name: Checkout code
|
|
21
|
-
uses: actions/checkout@v4
|
|
22
|
-
|
|
23
|
-
- name: Setup Node.js ${{ matrix.node-version }}
|
|
24
|
-
uses: actions/setup-node@v4
|
|
25
|
-
with:
|
|
26
|
-
node-version: ${{ matrix.node-version }}
|
|
27
|
-
cache: "npm"
|
|
28
|
-
|
|
29
|
-
- name: Install dependencies
|
|
30
|
-
run: npm ci
|
|
31
|
-
|
|
32
|
-
- name: Run ESLint
|
|
33
|
-
run: npm run lint
|
package/index.js
DELETED
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
-
import {
|
|
6
|
-
CallToolRequestSchema,
|
|
7
|
-
ListToolsRequestSchema,
|
|
8
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
-
import { spawn } from "child_process";
|
|
10
|
-
import path from "path";
|
|
11
|
-
import fs from "fs";
|
|
12
|
-
|
|
13
|
-
class FluffOSMCPServer {
|
|
14
|
-
constructor() {
|
|
15
|
-
this.server = new Server(
|
|
16
|
-
{
|
|
17
|
-
name: "fluffos-mcp-server",
|
|
18
|
-
version: "0.1.0",
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
capabilities: {
|
|
22
|
-
tools: {},
|
|
23
|
-
},
|
|
24
|
-
}
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
this.binDir = process.env.FLUFFOS_BIN_DIR;
|
|
28
|
-
this.configFile = process.env.MUD_RUNTIME_CONFIG_FILE;
|
|
29
|
-
this.docsDir = process.env.FLUFFOS_DOCS_DIR;
|
|
30
|
-
|
|
31
|
-
if (!this.binDir) {
|
|
32
|
-
console.error("Error: FLUFFOS_BIN_DIR environment variable not set");
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!this.configFile) {
|
|
37
|
-
console.error("Error: MUD_RUNTIME_CONFIG_FILE environment variable not set");
|
|
38
|
-
process.exit(1);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Parse mudlib directory from config file
|
|
42
|
-
this.mudlibDir = this.parseMudlibDir();
|
|
43
|
-
|
|
44
|
-
console.error(`FluffOS bin directory: ${this.binDir}`);
|
|
45
|
-
console.error(`FluffOS config file: ${this.configFile}`);
|
|
46
|
-
console.error(`Mudlib directory: ${this.mudlibDir || "(not found in config)"}`);
|
|
47
|
-
|
|
48
|
-
if (this.docsDir) {
|
|
49
|
-
console.error(`FluffOS docs directory: ${this.docsDir}`);
|
|
50
|
-
} else {
|
|
51
|
-
console.error(`FluffOS docs directory: not set (doc lookup disabled)`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
this.setupHandlers();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
parseMudlibDir() {
|
|
58
|
-
try {
|
|
59
|
-
const configContent = fs.readFileSync(this.configFile, "utf8");
|
|
60
|
-
const match = configContent.match(/^mudlib directory\s*:\s*(.+)$/m);
|
|
61
|
-
if (match) {
|
|
62
|
-
return match[1].trim();
|
|
63
|
-
}
|
|
64
|
-
} catch (err) {
|
|
65
|
-
console.error(`Warning: Could not parse mudlib directory from config: ${err.message}`);
|
|
66
|
-
}
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
normalizePath(lpcFile) {
|
|
71
|
-
// If we have a mudlib directory and the file path is absolute and starts with mudlib dir,
|
|
72
|
-
// convert it to a relative path
|
|
73
|
-
if (this.mudlibDir && path.isAbsolute(lpcFile) && lpcFile.startsWith(this.mudlibDir)) {
|
|
74
|
-
// Remove mudlib directory prefix and leading slash
|
|
75
|
-
return lpcFile.substring(this.mudlibDir.length).replace(/^\/+/, "");
|
|
76
|
-
}
|
|
77
|
-
// Otherwise return as-is (already relative or not under mudlib)
|
|
78
|
-
return lpcFile;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
setupHandlers() {
|
|
82
|
-
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
83
|
-
tools: [
|
|
84
|
-
{
|
|
85
|
-
name: "fluffos_validate",
|
|
86
|
-
description:
|
|
87
|
-
"Validate an LPC file using the FluffOS driver's symbol tool. Compiles the file and reports success or failure with any compilation errors. Fast and lightweight check for code validity.",
|
|
88
|
-
inputSchema: {
|
|
89
|
-
type: "object",
|
|
90
|
-
properties: {
|
|
91
|
-
file: {
|
|
92
|
-
type: "string",
|
|
93
|
-
description: "Absolute path to the LPC file to validate",
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
required: ["file"],
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
name: "fluffos_disassemble",
|
|
101
|
-
description:
|
|
102
|
-
"Disassemble an LPC file to show compiled bytecode using lpcc. Returns detailed bytecode, function tables, strings, and disassembly. Useful for debugging and understanding how code compiles.",
|
|
103
|
-
inputSchema: {
|
|
104
|
-
type: "object",
|
|
105
|
-
properties: {
|
|
106
|
-
file: {
|
|
107
|
-
type: "string",
|
|
108
|
-
description: "Absolute path to the LPC file to disassemble",
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
required: ["file"],
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
...(this.docsDir ? [{
|
|
115
|
-
name: "fluffos_doc_lookup",
|
|
116
|
-
description:
|
|
117
|
-
"Search FluffOS documentation for information about efuns, applies, concepts, etc. Searches markdown documentation files.",
|
|
118
|
-
inputSchema: {
|
|
119
|
-
type: "object",
|
|
120
|
-
properties: {
|
|
121
|
-
query: {
|
|
122
|
-
type: "string",
|
|
123
|
-
description: "Term to search for in documentation (e.g., 'call_out', 'mapping', 'socket')",
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
required: ["query"],
|
|
127
|
-
},
|
|
128
|
-
}] : []),
|
|
129
|
-
],
|
|
130
|
-
}));
|
|
131
|
-
|
|
132
|
-
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
133
|
-
const { name, arguments: args } = request.params;
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
switch (name) {
|
|
137
|
-
case "fluffos_validate": {
|
|
138
|
-
const result = await this.runSymbol(args.file);
|
|
139
|
-
return {
|
|
140
|
-
content: [
|
|
141
|
-
{
|
|
142
|
-
type: "text",
|
|
143
|
-
text: result,
|
|
144
|
-
},
|
|
145
|
-
],
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
case "fluffos_disassemble": {
|
|
150
|
-
const result = await this.runLpcc(args.file);
|
|
151
|
-
return {
|
|
152
|
-
content: [
|
|
153
|
-
{
|
|
154
|
-
type: "text",
|
|
155
|
-
text: result,
|
|
156
|
-
},
|
|
157
|
-
],
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
case "fluffos_doc_lookup": {
|
|
162
|
-
if (!this.docsDir) {
|
|
163
|
-
throw new Error("Documentation lookup is not available (FLUFFOS_DOCS_DIR not set)");
|
|
164
|
-
}
|
|
165
|
-
const result = await this.searchDocs(args.query);
|
|
166
|
-
return {
|
|
167
|
-
content: [
|
|
168
|
-
{
|
|
169
|
-
type: "text",
|
|
170
|
-
text: result,
|
|
171
|
-
},
|
|
172
|
-
],
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
default:
|
|
177
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
178
|
-
}
|
|
179
|
-
} catch (error) {
|
|
180
|
-
return {
|
|
181
|
-
content: [
|
|
182
|
-
{
|
|
183
|
-
type: "text",
|
|
184
|
-
text: `Error: ${error.message}`,
|
|
185
|
-
},
|
|
186
|
-
],
|
|
187
|
-
isError: true,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async runSymbol(lpcFile) {
|
|
194
|
-
return new Promise((resolve, reject) => {
|
|
195
|
-
const normalizedPath = this.normalizePath(lpcFile);
|
|
196
|
-
const symbolPath = path.join(this.binDir, "symbol");
|
|
197
|
-
const proc = spawn(symbolPath, [this.configFile, normalizedPath], {
|
|
198
|
-
cwd: path.dirname(this.configFile),
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
let stdout = "";
|
|
202
|
-
let stderr = "";
|
|
203
|
-
|
|
204
|
-
proc.stdout.on("data", (data) => {
|
|
205
|
-
stdout += data.toString();
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
proc.stderr.on("data", (data) => {
|
|
209
|
-
stderr += data.toString();
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
proc.on("close", (code) => {
|
|
213
|
-
const output = (stdout + stderr).trim();
|
|
214
|
-
|
|
215
|
-
if (code === 0) {
|
|
216
|
-
resolve(`✓ File validated successfully\n\n${output}`);
|
|
217
|
-
} else {
|
|
218
|
-
resolve(`✗ Validation failed (exit code: ${code})\n\n${output}`);
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
proc.on("error", (err) => {
|
|
223
|
-
reject(new Error(`Failed to run symbol: ${err.message}`));
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
async runLpcc(lpcFile) {
|
|
229
|
-
return new Promise((resolve, reject) => {
|
|
230
|
-
const normalizedPath = this.normalizePath(lpcFile);
|
|
231
|
-
const lpccPath = path.join(this.binDir, "lpcc");
|
|
232
|
-
const proc = spawn(lpccPath, [this.configFile, normalizedPath], {
|
|
233
|
-
cwd: path.dirname(this.configFile),
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
let stdout = "";
|
|
237
|
-
let stderr = "";
|
|
238
|
-
|
|
239
|
-
proc.stdout.on("data", (data) => {
|
|
240
|
-
stdout += data.toString();
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
proc.stderr.on("data", (data) => {
|
|
244
|
-
stderr += data.toString();
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
proc.on("close", (code) => {
|
|
248
|
-
const output = (stdout + stderr).trim();
|
|
249
|
-
|
|
250
|
-
if (code === 0) {
|
|
251
|
-
resolve(output);
|
|
252
|
-
} else {
|
|
253
|
-
resolve(`Error (exit code: ${code}):\n\n${output}`);
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
proc.on("error", (err) => {
|
|
258
|
-
reject(new Error(`Failed to run lpcc: ${err.message}`));
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
async searchDocs(query) {
|
|
264
|
-
return new Promise((resolve, reject) => {
|
|
265
|
-
const scriptPath = path.join(path.dirname(new URL(import.meta.url).pathname), "scripts", "search_docs.sh");
|
|
266
|
-
const proc = spawn(scriptPath, [this.docsDir, query]);
|
|
267
|
-
|
|
268
|
-
let stdout = "";
|
|
269
|
-
let stderr = "";
|
|
270
|
-
|
|
271
|
-
proc.stdout.on("data", (data) => {
|
|
272
|
-
stdout += data.toString();
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
proc.stderr.on("data", (data) => {
|
|
276
|
-
stderr += data.toString();
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
proc.on("close", (code) => {
|
|
280
|
-
if (code === 0) {
|
|
281
|
-
if (stdout.trim()) {
|
|
282
|
-
resolve(`Found documentation for "${query}":\n\n${stdout}`);
|
|
283
|
-
} else {
|
|
284
|
-
resolve(`No documentation found for "${query}".`);
|
|
285
|
-
}
|
|
286
|
-
} else {
|
|
287
|
-
resolve(`Error searching documentation:\n${stderr || stdout}`);
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
proc.on("error", (err) => {
|
|
292
|
-
reject(new Error(`Failed to search docs: ${err.message}`));
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
async run() {
|
|
298
|
-
const transport = new StdioServerTransport();
|
|
299
|
-
await this.server.connect(transport);
|
|
300
|
-
|
|
301
|
-
console.error("FluffOS MCP Server running on stdio");
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const server = new FluffOSMCPServer();
|
|
306
|
-
server.run().catch(console.error);
|