poi-plugin-mcp 0.2.0 → 0.2.2
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/README.md +21 -4
- package/lib/poi-http-bridge.js +58 -6
- package/mcp-server.js +68 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
# poi-plugin-mcp
|
|
2
2
|
|
|
3
|
-
Poi plugin that starts a local HTTP bridge inside Poi so CLI/MCP agents can read current KanColle data.
|
|
3
|
+
Poi plugin that starts a local HTTP/MCP bridge inside Poi so CLI/MCP agents can read current KanColle data.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Install it from Poi's plugin manager as:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
poi-plugin-mcp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install it into Poi's plugin directory with npm:
|
|
14
|
+
|
|
15
|
+
```powershell
|
|
16
|
+
cd "$env:APPDATA\poi\plugins"
|
|
17
|
+
npm install poi-plugin-mcp
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then restart Poi and enable `MCP Data Bridge` if needed.
|
|
21
|
+
|
|
22
|
+
## Development Install
|
|
23
|
+
|
|
24
|
+
When working from this repository, install from the repo root:
|
|
8
25
|
|
|
9
26
|
```powershell
|
|
10
27
|
npm install
|
|
11
28
|
npm run install:poi
|
|
12
29
|
```
|
|
13
30
|
|
|
14
|
-
Then restart Poi and enable `MCP
|
|
31
|
+
Then restart Poi and enable `MCP Data Bridge` if needed.
|
|
15
32
|
|
|
16
33
|
The installer creates:
|
|
17
34
|
|
package/lib/poi-http-bridge.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require('fs')
|
|
|
2
2
|
const http = require('http')
|
|
3
3
|
const os = require('os')
|
|
4
4
|
const path = require('path')
|
|
5
|
+
const packageJson = require('../package.json')
|
|
5
6
|
|
|
6
7
|
const DEFAULT_PORT = 17777
|
|
7
8
|
const DEFAULT_PORT_FILE = path.join(os.homedir(), '.poi-mcp', 'port')
|
|
@@ -228,7 +229,7 @@ function handleMcpMessage(message, readStore, plannerFile) {
|
|
|
228
229
|
resources: { subscribe: false },
|
|
229
230
|
tools: {},
|
|
230
231
|
},
|
|
231
|
-
serverInfo: { name: 'poi-plugin-mcp', version:
|
|
232
|
+
serverInfo: { name: 'poi-plugin-mcp', version: packageJson.version },
|
|
232
233
|
})
|
|
233
234
|
|
|
234
235
|
case 'notifications/initialized':
|
|
@@ -333,8 +334,16 @@ const MCP_TOOLS = [
|
|
|
333
334
|
},
|
|
334
335
|
{
|
|
335
336
|
name: 'get_all',
|
|
336
|
-
description: 'Get account basics, fleets, ships, equipment, resources, quests, airbase, and names.',
|
|
337
|
-
inputSchema: {
|
|
337
|
+
description: 'Get account basics, fleets, ships, equipment, resources, quests, airbase, and names. Optionally include master, event, and planner data.',
|
|
338
|
+
inputSchema: {
|
|
339
|
+
type: 'object',
|
|
340
|
+
properties: {
|
|
341
|
+
include: {
|
|
342
|
+
type: 'array',
|
|
343
|
+
items: { type: 'string', enum: ['master', 'event', 'planner'] },
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
},
|
|
338
347
|
},
|
|
339
348
|
]
|
|
340
349
|
|
|
@@ -349,7 +358,7 @@ function callMcpTool(toolName, args, readStore, plannerFile) {
|
|
|
349
358
|
case 'get_resources':
|
|
350
359
|
return { value: readBridgeData('/resources', readStore, plannerFile) }
|
|
351
360
|
case 'get_all':
|
|
352
|
-
return { value:
|
|
361
|
+
return { value: buildAllPayload(args || {}, readStore, plannerFile) }
|
|
353
362
|
default:
|
|
354
363
|
return { error: `Unknown tool: ${toolName}` }
|
|
355
364
|
}
|
|
@@ -446,28 +455,69 @@ function buildFleetStatus(args, readStore) {
|
|
|
446
455
|
|
|
447
456
|
function searchShips(args, readStore) {
|
|
448
457
|
const store = readStore()
|
|
458
|
+
const master = extractMasterData(store)
|
|
449
459
|
const ships = Object.values((store.info && store.info.ships) || {}).filter((ship) => {
|
|
450
460
|
if (!ship) return false
|
|
451
461
|
if (args.minLevel != null && ship.api_lv < Number(args.minLevel)) return false
|
|
452
462
|
if (args.maxLevel != null && ship.api_lv > Number(args.maxLevel)) return false
|
|
453
463
|
if (args.minMorale != null && ship.api_cond < Number(args.minMorale)) return false
|
|
454
464
|
return true
|
|
455
|
-
})
|
|
465
|
+
}).map((ship) => enrichShip(ship, master))
|
|
456
466
|
|
|
457
467
|
return { total: ships.length, ships }
|
|
458
468
|
}
|
|
459
469
|
|
|
460
470
|
function searchEquipment(args, readStore) {
|
|
461
471
|
const store = readStore()
|
|
472
|
+
const master = extractMasterData(store)
|
|
462
473
|
const equipment = Object.values((store.info && store.info.equips) || {}).filter((equip) => {
|
|
463
474
|
if (!equip) return false
|
|
464
475
|
if (args.minLevel != null && (equip.api_level || 0) < Number(args.minLevel)) return false
|
|
465
476
|
return true
|
|
466
|
-
})
|
|
477
|
+
}).map((equip) => enrichEquipment(equip, master))
|
|
467
478
|
|
|
468
479
|
return { total: equipment.length, equipment }
|
|
469
480
|
}
|
|
470
481
|
|
|
482
|
+
function buildAllPayload(args, readStore, plannerFile) {
|
|
483
|
+
const payload = readBridgeData('/all', readStore, plannerFile)
|
|
484
|
+
const include = Array.isArray(args.include) ? new Set(args.include) : new Set()
|
|
485
|
+
|
|
486
|
+
if (include.has('master')) payload.master = readBridgeData('/master', readStore, plannerFile)
|
|
487
|
+
if (include.has('event')) payload.event = readBridgeData('/event', readStore, plannerFile)
|
|
488
|
+
if (include.has('planner')) payload.planner = readBridgeData('/planner', readStore, plannerFile)
|
|
489
|
+
|
|
490
|
+
return payload
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function enrichShip(ship, master) {
|
|
494
|
+
const masterShip = master.ships && master.ships[ship.api_ship_id]
|
|
495
|
+
const shipType = masterShip && master.shipTypes && master.shipTypes[masterShip.api_stype]
|
|
496
|
+
|
|
497
|
+
return {
|
|
498
|
+
...ship,
|
|
499
|
+
instanceId: ship.api_id,
|
|
500
|
+
masterId: ship.api_ship_id,
|
|
501
|
+
name: (masterShip && masterShip.api_name) || '',
|
|
502
|
+
typeName: (shipType && shipType.api_name) || '',
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function enrichEquipment(equip, master) {
|
|
507
|
+
const masterEquip = master.equipment && master.equipment[equip.api_slotitem_id]
|
|
508
|
+
const typeIds = masterEquip && Array.isArray(masterEquip.api_type) ? masterEquip.api_type : []
|
|
509
|
+
const typeId = typeIds[2] || typeIds[1] || typeIds[0]
|
|
510
|
+
const equipType = typeId && master.equipmentTypes && master.equipmentTypes[typeId]
|
|
511
|
+
|
|
512
|
+
return {
|
|
513
|
+
...equip,
|
|
514
|
+
instanceId: equip.api_id,
|
|
515
|
+
masterId: equip.api_slotitem_id,
|
|
516
|
+
name: (masterEquip && masterEquip.api_name) || '',
|
|
517
|
+
typeName: (equipType && equipType.api_name) || '',
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
471
521
|
function jsonRpcResult(id, result) {
|
|
472
522
|
return { jsonrpc: JSONRPC_VERSION, id, result }
|
|
473
523
|
}
|
|
@@ -547,6 +597,8 @@ function extractEventData(store) {
|
|
|
547
597
|
const tag = tags[area - 1] || emptyTag(area)
|
|
548
598
|
|
|
549
599
|
ships[ship.api_id] = {
|
|
600
|
+
instanceId: ship.api_id,
|
|
601
|
+
masterId: ship.api_ship_id,
|
|
550
602
|
shipId: ship.api_id,
|
|
551
603
|
modelId: ship.api_ship_id,
|
|
552
604
|
area,
|
package/mcp-server.js
CHANGED
|
@@ -57,6 +57,7 @@ const os = require('os')
|
|
|
57
57
|
const path = require('path')
|
|
58
58
|
const fs = require('fs')
|
|
59
59
|
const http = require('http')
|
|
60
|
+
const packageJson = require('./package.json')
|
|
60
61
|
|
|
61
62
|
const PORT_FILE = path.join(os.homedir(), '.poi-mcp', 'port')
|
|
62
63
|
|
|
@@ -139,6 +140,53 @@ function getInjectScript() {
|
|
|
139
140
|
].join('\n')
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
async function fetchMasterData() {
|
|
144
|
+
try {
|
|
145
|
+
return await fetchFromPoi('/master')
|
|
146
|
+
} catch (_) {
|
|
147
|
+
return { ships: {}, equipment: {}, shipTypes: {}, equipmentTypes: {} }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function enrichShip(ship, master) {
|
|
152
|
+
const masterShip = master.ships && master.ships[ship.api_ship_id]
|
|
153
|
+
const shipType = masterShip && master.shipTypes && master.shipTypes[masterShip.api_stype]
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
...ship,
|
|
157
|
+
instanceId: ship.api_id,
|
|
158
|
+
masterId: ship.api_ship_id,
|
|
159
|
+
name: (masterShip && masterShip.api_name) || '',
|
|
160
|
+
typeName: (shipType && shipType.api_name) || '',
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function enrichEquipment(equip, master) {
|
|
165
|
+
const masterEquip = master.equipment && master.equipment[equip.api_slotitem_id]
|
|
166
|
+
const typeIds = masterEquip && Array.isArray(masterEquip.api_type) ? masterEquip.api_type : []
|
|
167
|
+
const typeId = typeIds[2] || typeIds[1] || typeIds[0]
|
|
168
|
+
const equipType = typeId && master.equipmentTypes && master.equipmentTypes[typeId]
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
...equip,
|
|
172
|
+
instanceId: equip.api_id,
|
|
173
|
+
masterId: equip.api_slotitem_id,
|
|
174
|
+
name: (masterEquip && masterEquip.api_name) || '',
|
|
175
|
+
typeName: (equipType && equipType.api_name) || '',
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function fetchAllData(args = {}) {
|
|
180
|
+
const payload = await fetchFromPoi('/all')
|
|
181
|
+
const include = Array.isArray(args.include) ? new Set(args.include) : new Set()
|
|
182
|
+
|
|
183
|
+
if (include.has('master')) payload.master = await fetchFromPoi('/master')
|
|
184
|
+
if (include.has('event')) payload.event = await fetchFromPoi('/event')
|
|
185
|
+
if (include.has('planner')) payload.planner = await fetchFromPoi('/planner')
|
|
186
|
+
|
|
187
|
+
return payload
|
|
188
|
+
}
|
|
189
|
+
|
|
142
190
|
// ─── MCP Protocol ────────────────────────────────────────────────────────────
|
|
143
191
|
|
|
144
192
|
// Minimum viable MCP stdio server — no external dependencies
|
|
@@ -161,7 +209,7 @@ function sendLog(text) {
|
|
|
161
209
|
// ─── Main ────────────────────────────────────────────────────────────────────
|
|
162
210
|
|
|
163
211
|
async function main() {
|
|
164
|
-
sendLog(
|
|
212
|
+
sendLog(`POI MCP Server v${packageJson.version}`)
|
|
165
213
|
sendLog('Checking POI data API...')
|
|
166
214
|
|
|
167
215
|
const port = getPoiPort()
|
|
@@ -226,23 +274,25 @@ async function main() {
|
|
|
226
274
|
|
|
227
275
|
search_ships: async (args) => {
|
|
228
276
|
const ships = await fetchFromPoi('/ships')
|
|
277
|
+
const master = await fetchMasterData()
|
|
229
278
|
const results = Object.values(ships).filter(s => {
|
|
230
279
|
if (!s) return false
|
|
231
280
|
if (args.minLevel != null && s.api_lv < args.minLevel) return false
|
|
232
281
|
if (args.maxLevel != null && s.api_lv > args.maxLevel) return false
|
|
233
282
|
if (args.minMorale != null && s.api_cond < args.minMorale) return false
|
|
234
283
|
return true
|
|
235
|
-
})
|
|
284
|
+
}).map(s => enrichShip(s, master))
|
|
236
285
|
return { total: results.length, ships: results }
|
|
237
286
|
},
|
|
238
287
|
|
|
239
288
|
search_equipment: async (args) => {
|
|
240
289
|
const equips = await fetchFromPoi('/equipment')
|
|
290
|
+
const master = await fetchMasterData()
|
|
241
291
|
const results = Object.values(equips).filter(e => {
|
|
242
292
|
if (!e) return false
|
|
243
293
|
if (args.minLevel != null && (e.api_level || 0) < args.minLevel) return false
|
|
244
294
|
return true
|
|
245
|
-
})
|
|
295
|
+
}).map(e => enrichEquipment(e, master))
|
|
246
296
|
return { total: results.length, equipment: results }
|
|
247
297
|
},
|
|
248
298
|
|
|
@@ -250,14 +300,15 @@ async function main() {
|
|
|
250
300
|
return await fetchFromPoi('/resources')
|
|
251
301
|
},
|
|
252
302
|
|
|
253
|
-
get_all: async () => {
|
|
254
|
-
return await
|
|
303
|
+
get_all: async (args) => {
|
|
304
|
+
return await fetchAllData(args)
|
|
255
305
|
}
|
|
256
306
|
}
|
|
257
307
|
|
|
258
308
|
const resourceUris = [
|
|
259
309
|
'poi://fleets', 'poi://ships', 'poi://equipment',
|
|
260
|
-
'poi://resources', 'poi://quests', 'poi://airbase', 'poi://basic',
|
|
310
|
+
'poi://resources', 'poi://quests', 'poi://airbase', 'poi://basic',
|
|
311
|
+
'poi://names', 'poi://master', 'poi://event', 'poi://planner', 'poi://all'
|
|
261
312
|
]
|
|
262
313
|
|
|
263
314
|
// ── JSON-RPC over stdio ──────────────────────────────────────────────
|
|
@@ -282,7 +333,7 @@ async function main() {
|
|
|
282
333
|
resources: { subscribe: false },
|
|
283
334
|
tools: {}
|
|
284
335
|
},
|
|
285
|
-
serverInfo: { name: 'poi-mcp', version:
|
|
336
|
+
serverInfo: { name: 'poi-mcp', version: packageJson.version }
|
|
286
337
|
})
|
|
287
338
|
break
|
|
288
339
|
|
|
@@ -357,8 +408,16 @@ async function main() {
|
|
|
357
408
|
},
|
|
358
409
|
{
|
|
359
410
|
name: 'get_all',
|
|
360
|
-
description: '
|
|
361
|
-
inputSchema: {
|
|
411
|
+
description: '获取所有数据(舰队/舰娘/装备/资源/任务/陆航),可选包含 master/event/planner',
|
|
412
|
+
inputSchema: {
|
|
413
|
+
type: 'object',
|
|
414
|
+
properties: {
|
|
415
|
+
include: {
|
|
416
|
+
type: 'array',
|
|
417
|
+
items: { type: 'string', enum: ['master', 'event', 'planner'] }
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
362
421
|
}
|
|
363
422
|
]
|
|
364
423
|
})
|