@gesslar/fluffos-mcp 0.1.5 → 0.2.1
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/.claude/settings.local.json +7 -0
- package/package.json +6 -4
- package/src/index.js +112 -124
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "gesslar",
|
|
6
6
|
"url": "https://gesslar.dev"
|
|
7
7
|
},
|
|
8
|
-
"version": "0.1
|
|
8
|
+
"version": "0.2.1",
|
|
9
9
|
"license": "Unlicense",
|
|
10
10
|
"homepage": "https://github.com/gesslar/fluffos-mcp#readme",
|
|
11
11
|
"repository": {
|
|
@@ -32,10 +32,12 @@
|
|
|
32
32
|
"fluffos-mcp": "./src/index.js"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@
|
|
35
|
+
"@gesslar/toolkit": "^3.23.0",
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
37
|
+
"zod": "^4.3.5"
|
|
36
38
|
},
|
|
37
39
|
"devDependencies": {
|
|
38
|
-
"@gesslar/uglier": "^
|
|
40
|
+
"@gesslar/uglier": "^1.1.0",
|
|
39
41
|
"eslint": "^9.39.2"
|
|
40
42
|
},
|
|
41
43
|
"scripts": {
|
|
@@ -43,7 +45,7 @@
|
|
|
43
45
|
"lint": "eslint src/",
|
|
44
46
|
"lint:fix": "eslint src/ --fix",
|
|
45
47
|
"submit": "pnpm publish --access public --//registry.npmjs.org/:_authToken=\"${NPM_ACCESS_TOKEN}\"",
|
|
46
|
-
"update": "pnpm
|
|
48
|
+
"update": "pnpm self-update && pnpx npm-check-updates -u && pnpm install",
|
|
47
49
|
"pr": "gt submit -p --ai",
|
|
48
50
|
"patch": "pnpm version patch",
|
|
49
51
|
"minor": "pnpm version minor",
|
package/src/index.js
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js"
|
|
4
4
|
import {StdioServerTransport} from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
5
|
-
import
|
|
6
|
-
CallToolRequestSchema,
|
|
7
|
-
ListToolsRequestSchema,
|
|
8
|
-
} from "@modelcontextprotocol/sdk/types.js"
|
|
5
|
+
import * as z from "zod/v4"
|
|
9
6
|
import {spawn} from "child_process"
|
|
10
|
-
import
|
|
11
|
-
import fs from "fs"
|
|
7
|
+
import {FileObject, DirectoryObject} from "@gesslar/toolkit"
|
|
12
8
|
|
|
13
9
|
class FluffOSMCPServer {
|
|
14
10
|
constructor() {
|
|
15
|
-
this.server = new
|
|
11
|
+
this.server = new McpServer(
|
|
16
12
|
{
|
|
17
13
|
name: "fluffos-mcp-server",
|
|
18
14
|
version: "0.1.0",
|
|
@@ -28,7 +24,9 @@ class FluffOSMCPServer {
|
|
|
28
24
|
this.configFile = process.env.MUD_RUNTIME_CONFIG_FILE
|
|
29
25
|
this.docsDir = process.env.FLUFFOS_DOCS_DIR
|
|
30
26
|
this.mudlibDir = null
|
|
27
|
+
}
|
|
31
28
|
|
|
29
|
+
async initialize() {
|
|
32
30
|
if(!this.binDir) {
|
|
33
31
|
console.error("Error: FLUFFOS_BIN_DIR environment variable not set")
|
|
34
32
|
process.exit(1)
|
|
@@ -40,28 +38,29 @@ class FluffOSMCPServer {
|
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
// Parse mudlib directory from config file
|
|
43
|
-
this.mudlibDir = this.parseMudlibDir()
|
|
41
|
+
this.mudlibDir = await this.parseMudlibDir()
|
|
44
42
|
|
|
45
43
|
console.error(`FluffOS bin directory: ${this.binDir}`)
|
|
46
44
|
console.error(`FluffOS config file: ${this.configFile}`)
|
|
47
45
|
console.error(`Mudlib directory: ${this.mudlibDir || "(not found in config)"}`)
|
|
48
46
|
|
|
49
|
-
if(this.docsDir)
|
|
47
|
+
if(this.docsDir)
|
|
50
48
|
console.error(`FluffOS docs directory: ${this.docsDir}`)
|
|
51
|
-
|
|
49
|
+
else
|
|
52
50
|
console.error(`FluffOS docs directory: not set (doc lookup disabled)`)
|
|
53
|
-
}
|
|
54
51
|
|
|
55
|
-
this.
|
|
52
|
+
this.setupTools()
|
|
56
53
|
}
|
|
57
54
|
|
|
58
|
-
parseMudlibDir() {
|
|
55
|
+
async parseMudlibDir() {
|
|
59
56
|
try {
|
|
60
|
-
const
|
|
57
|
+
const configFile = new FileObject(this.configFile)
|
|
58
|
+
const configContent = await configFile.read()
|
|
61
59
|
const match = configContent.match(/^mudlib directory\s*:\s*(.+)$/m)
|
|
62
|
-
|
|
60
|
+
|
|
61
|
+
if(match)
|
|
63
62
|
return match[1].trim()
|
|
64
|
-
|
|
63
|
+
|
|
65
64
|
} catch(err) {
|
|
66
65
|
console.error(`Warning: Could not parse mudlib directory from config: ${err.message}`)
|
|
67
66
|
}
|
|
@@ -73,7 +72,7 @@ class FluffOSMCPServer {
|
|
|
73
72
|
// If we have a mudlib directory and the file path is absolute and starts with mudlib dir,
|
|
74
73
|
// convert it to a relative path
|
|
75
74
|
if(this.mudlibDir &&
|
|
76
|
-
|
|
75
|
+
lpcFile.startsWith("/") &&
|
|
77
76
|
lpcFile.startsWith(this.mudlibDir)
|
|
78
77
|
) {
|
|
79
78
|
// Remove mudlib directory prefix and leading slash
|
|
@@ -84,109 +83,57 @@ class FluffOSMCPServer {
|
|
|
84
83
|
return lpcFile
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
name: "fluffos_disassemble",
|
|
109
|
-
description:
|
|
110
|
-
"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.",
|
|
111
|
-
inputSchema: {
|
|
112
|
-
type: "object",
|
|
113
|
-
properties: {
|
|
114
|
-
file: {
|
|
115
|
-
type: "string",
|
|
116
|
-
description: "Absolute path to the LPC file to disassemble",
|
|
117
|
-
},
|
|
86
|
+
setupTools() {
|
|
87
|
+
// Register validate tool
|
|
88
|
+
this.server.registerTool("fluffos_validate", {
|
|
89
|
+
description: "Validate an LPC file using the FluffOS driver's symbol tool. " +
|
|
90
|
+
"Compiles the file and reports success or failure with any " +
|
|
91
|
+
"compilation errors. Fast and lightweight check for code validity.",
|
|
92
|
+
inputSchema: z.object({
|
|
93
|
+
file: z.string().describe("Absolute path to the LPC file to validate"),
|
|
94
|
+
}),
|
|
95
|
+
}, async({file}) => {
|
|
96
|
+
try {
|
|
97
|
+
const result = await this.runSymbol(file)
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
content: [
|
|
101
|
+
{
|
|
102
|
+
type: "text",
|
|
103
|
+
text: result,
|
|
118
104
|
},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
type: "object",
|
|
128
|
-
properties: {
|
|
129
|
-
query: {
|
|
130
|
-
type: "string",
|
|
131
|
-
description: "Term to search for in documentation (e.g., 'call_out', 'mapping', 'socket')",
|
|
132
|
-
},
|
|
105
|
+
],
|
|
106
|
+
}
|
|
107
|
+
} catch(error) {
|
|
108
|
+
return {
|
|
109
|
+
content: [
|
|
110
|
+
{
|
|
111
|
+
type: "text",
|
|
112
|
+
text: `Error: ${error.message}`,
|
|
133
113
|
},
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
this.server.setRequestHandler(CallToolRequestSchema, async request => {
|
|
141
|
-
const {name, arguments: args} = request.params
|
|
114
|
+
],
|
|
115
|
+
isError: true,
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
})
|
|
142
119
|
|
|
120
|
+
// Register disassemble tool
|
|
121
|
+
this.server.registerTool("fluffos_disassemble", {
|
|
122
|
+
description: "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.",
|
|
123
|
+
inputSchema: z.object({
|
|
124
|
+
file: z.string().describe("Absolute path to the LPC file to disassemble"),
|
|
125
|
+
}),
|
|
126
|
+
}, async({file}) => {
|
|
143
127
|
try {
|
|
144
|
-
|
|
145
|
-
case "fluffos_validate": {
|
|
146
|
-
const result = await this.runSymbol(args.file)
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
content: [
|
|
150
|
-
{
|
|
151
|
-
type: "text",
|
|
152
|
-
text: result,
|
|
153
|
-
},
|
|
154
|
-
],
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
case "fluffos_disassemble": {
|
|
159
|
-
const result = await this.runLpcc(args.file)
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
content: [
|
|
163
|
-
{
|
|
164
|
-
type: "text",
|
|
165
|
-
text: result,
|
|
166
|
-
},
|
|
167
|
-
],
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
case "fluffos_doc_lookup": {
|
|
172
|
-
if(!this.docsDir) {
|
|
173
|
-
throw new Error("Documentation lookup is not available (FLUFFOS_DOCS_DIR not set)")
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const result = await this.searchDocs(args.query)
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
content: [
|
|
180
|
-
{
|
|
181
|
-
type: "text",
|
|
182
|
-
text: result,
|
|
183
|
-
},
|
|
184
|
-
],
|
|
185
|
-
}
|
|
186
|
-
}
|
|
128
|
+
const result = await this.runLpcc(file)
|
|
187
129
|
|
|
188
|
-
|
|
189
|
-
|
|
130
|
+
return {
|
|
131
|
+
content: [
|
|
132
|
+
{
|
|
133
|
+
type: "text",
|
|
134
|
+
text: result,
|
|
135
|
+
},
|
|
136
|
+
],
|
|
190
137
|
}
|
|
191
138
|
} catch(error) {
|
|
192
139
|
return {
|
|
@@ -200,14 +147,49 @@ class FluffOSMCPServer {
|
|
|
200
147
|
}
|
|
201
148
|
}
|
|
202
149
|
})
|
|
150
|
+
|
|
151
|
+
// Register doc lookup tool (conditional)
|
|
152
|
+
if(this.docsDir) {
|
|
153
|
+
this.server.registerTool("fluffos_doc_lookup", {
|
|
154
|
+
description: "Search FluffOS documentation for information about efuns, applies, concepts, etc. Searches markdown documentation files.",
|
|
155
|
+
inputSchema: z.object({
|
|
156
|
+
query: z.string().describe("Term to search for in documentation (e.g., 'call_out', 'mapping', 'socket')"),
|
|
157
|
+
}),
|
|
158
|
+
}, async({query}) => {
|
|
159
|
+
try {
|
|
160
|
+
const result = await this.searchDocs(query)
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
content: [
|
|
164
|
+
{
|
|
165
|
+
type: "text",
|
|
166
|
+
text: result,
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
}
|
|
170
|
+
} catch(error) {
|
|
171
|
+
return {
|
|
172
|
+
content: [
|
|
173
|
+
{
|
|
174
|
+
type: "text",
|
|
175
|
+
text: `Error: ${error.message}`,
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
isError: true,
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
}
|
|
203
183
|
}
|
|
204
184
|
|
|
205
185
|
async runSymbol(lpcFile) {
|
|
206
186
|
return new Promise((resolve, reject) => {
|
|
207
187
|
const normalizedPath = this.normalizePath(lpcFile)
|
|
208
|
-
const
|
|
188
|
+
const binDir = new DirectoryObject(this.binDir)
|
|
189
|
+
const symbolPath = binDir.getFile("symbol").path
|
|
190
|
+
const configFile = new FileObject(this.configFile)
|
|
209
191
|
const proc = spawn(symbolPath, [this.configFile, normalizedPath], {
|
|
210
|
-
cwd:
|
|
192
|
+
cwd: configFile.parentPath,
|
|
211
193
|
})
|
|
212
194
|
|
|
213
195
|
let stdout = ""
|
|
@@ -224,11 +206,11 @@ class FluffOSMCPServer {
|
|
|
224
206
|
proc.on("close", code => {
|
|
225
207
|
const output = (stdout + stderr).trim()
|
|
226
208
|
|
|
227
|
-
if(code === 0)
|
|
209
|
+
if(code === 0)
|
|
228
210
|
resolve(`✓ File validated successfully\n\n${output}`)
|
|
229
|
-
|
|
211
|
+
else
|
|
230
212
|
resolve(`✗ Validation failed (exit code: ${code})\n\n${output}`)
|
|
231
|
-
|
|
213
|
+
|
|
232
214
|
})
|
|
233
215
|
|
|
234
216
|
proc.on("error", err => {
|
|
@@ -240,9 +222,11 @@ class FluffOSMCPServer {
|
|
|
240
222
|
async runLpcc(lpcFile) {
|
|
241
223
|
return new Promise((resolve, reject) => {
|
|
242
224
|
const normalizedPath = this.normalizePath(lpcFile)
|
|
243
|
-
const
|
|
225
|
+
const binDir = new DirectoryObject(this.binDir)
|
|
226
|
+
const lpccPath = binDir.getFile("lpcc").path
|
|
227
|
+
const configFile = new FileObject(this.configFile)
|
|
244
228
|
const proc = spawn(lpccPath, [this.configFile, normalizedPath], {
|
|
245
|
-
cwd:
|
|
229
|
+
cwd: configFile.parentPath,
|
|
246
230
|
})
|
|
247
231
|
|
|
248
232
|
let stdout = ""
|
|
@@ -274,7 +258,9 @@ class FluffOSMCPServer {
|
|
|
274
258
|
|
|
275
259
|
async searchDocs(query) {
|
|
276
260
|
return new Promise((resolve, reject) => {
|
|
277
|
-
const
|
|
261
|
+
const moduleFile = new FileObject(new URL(import.meta.url).pathname)
|
|
262
|
+
const scriptsDir = moduleFile.parent.getDirectory("scripts")
|
|
263
|
+
const scriptPath = scriptsDir.getFile("search_docs.sh").path
|
|
278
264
|
const proc = spawn(scriptPath, [this.docsDir, query])
|
|
279
265
|
|
|
280
266
|
let stdout = ""
|
|
@@ -307,6 +293,8 @@ class FluffOSMCPServer {
|
|
|
307
293
|
}
|
|
308
294
|
|
|
309
295
|
async run() {
|
|
296
|
+
await this.initialize()
|
|
297
|
+
|
|
310
298
|
const transport = new StdioServerTransport()
|
|
311
299
|
await this.server.connect(transport)
|
|
312
300
|
|