@gesslar/fluffos-mcp 0.1.2 → 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.
@@ -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/README.md CHANGED
@@ -253,8 +253,8 @@ PRs welcome! This is a simple wrapper that can be extended with more FluffOS too
253
253
 
254
254
  ## Credits
255
255
 
256
- - **FluffOS Team** - For the amazing driver and CLI tools
257
- - [Model Context Protocol](https://modelcontextprotocol.io/) - Making this integration possible
256
+ - [**FluffOS Team**](<https://github.com/fluffos/fluffos>) - For the amazing driver and CLI tools
257
+ - [**Model Context Protocol**](<https://modelcontextprotocol.io/>) - Making this integration possible
258
258
 
259
259
  ## ~~License~~
260
260
 
package/eslint.config.js CHANGED
@@ -1,167 +1,11 @@
1
- import js from "@eslint/js"
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
- js.configs.recommended,
8
- jsdoc.configs['flat/recommended'], {
9
- name: "gesslar/uglier/ignores",
10
- ignores: [],
11
- }, {
12
- name: "gesslar/uglier/languageOptions",
13
- languageOptions: {
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.2",
4
3
  "description": "MCP server for FluffOS driver tools - validate and disassemble LPC code",
5
- "main": "src/index.js",
6
- "type": "module",
7
- "bin": {
8
- "fluffos-mcp": "./src/index.js"
4
+ "author": {
5
+ "name": "gesslar",
6
+ "url": "https://gesslar.dev"
9
7
  },
10
- "scripts": {
11
- "start": "node src/index.js",
12
- "lint": "eslint src/",
13
- "lint:fix": "eslint src/ --fix",
14
- "submit": "npm publish --access public",
15
- "update": "npx npm-check-updates -u && npm install",
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
- "author": "gesslar",
33
- "license": "Unlicense",
34
- "repository": {
35
- "type": "git",
36
- "url": "https://github.com/gesslar/fluffos-mcp.git"
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.20.2"
35
+ "@modelcontextprotocol/sdk": "^1.25.1"
40
36
  },
41
37
  "devDependencies": {
42
- "@eslint/js": "^9.39.0",
43
- "@stylistic/eslint-plugin": "^5.5.0",
44
- "eslint-plugin-jsdoc": "^61.1.11",
45
- "globals": "^16.5.0"
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
+ }
File without changes
@@ -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);