mikser-io-mcp 1.23.1 → 2.0.0
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/index.js +43 -21
- package/package.json +2 -2
- package/preview.js +10 -8
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// MCP substrate for mikser-io. The mcp plugin exposes
|
|
2
|
-
//
|
|
2
|
+
// the 'mcp' service (a substrate object) at factory time so other
|
|
3
3
|
// plugins can register tools / resources / prompts against it at their
|
|
4
4
|
// onLoaded hook with the same shape as the SDK's McpServer.
|
|
5
5
|
//
|
|
@@ -63,6 +63,8 @@ import {
|
|
|
63
63
|
useProvenance,
|
|
64
64
|
sourcesBehind,
|
|
65
65
|
sourcesOf,
|
|
66
|
+
provideService,
|
|
67
|
+
useService,
|
|
66
68
|
registerTool as coreRegisterTool,
|
|
67
69
|
toolNames as coreToolNames,
|
|
68
70
|
toolSchema as coreToolSchema,
|
|
@@ -259,7 +261,7 @@ function actingPrincipal() {
|
|
|
259
261
|
// everything else agree about who is calling.
|
|
260
262
|
const principal = authContext.getStore()?.principal
|
|
261
263
|
if (!principal) return 'agent'
|
|
262
|
-
const role = actingRole(principal.roles ?? [],
|
|
264
|
+
const role = actingRole(principal.roles ?? [], useService('roles')?.catalogue ?? {})
|
|
263
265
|
const subject = principal.subject ?? 'agent'
|
|
264
266
|
return role ? `${subject} (${role})` : subject
|
|
265
267
|
}
|
|
@@ -333,11 +335,24 @@ export function createMcpSubstrate() {
|
|
|
333
335
|
const schema = coreToolSchema(name)
|
|
334
336
|
if (!schema) continue
|
|
335
337
|
try {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
338
|
+
// normalizeInputSchema, not zodShapeFrom: a plugin registering
|
|
339
|
+
// against the engine may describe its parameters EITHER in the
|
|
340
|
+
// engine's neutral vocabulary or in zod. zodShapeFrom assumes
|
|
341
|
+
// neutral, so a real zod shape came through it as a bag of
|
|
342
|
+
// optional strings — every constraint dropped, no error, and a
|
|
343
|
+
// tool that looked registered but refused nothing.
|
|
344
|
+
let inputSchema = normalizeInputSchema(schema.inputSchema)
|
|
345
|
+
let handler = (args) => coreInvokeTool(name, args)
|
|
346
|
+
// `mutates: true` reaches here now that the engine registry
|
|
347
|
+
// carries the whole definition. Without this a mutating tool
|
|
348
|
+
// registered against the engine would take no changeSet, and
|
|
349
|
+
// its writes would be unattributable — the one failure nothing
|
|
350
|
+
// downstream can repair.
|
|
351
|
+
if (schema.mutates) {
|
|
352
|
+
inputSchema = withChangeSetParams(inputSchema)
|
|
353
|
+
handler = wrapMutatingHandler(handler)
|
|
354
|
+
}
|
|
355
|
+
server.registerTool(exposed, { description: schema.description, inputSchema }, handler)
|
|
341
356
|
bound.tools++
|
|
342
357
|
} catch (err) {
|
|
343
358
|
runtime.engine?.logger?.debug(
|
|
@@ -828,8 +843,8 @@ function authStatus() {
|
|
|
828
843
|
const authority = describeAuthority({
|
|
829
844
|
capabilities: principal.capabilities,
|
|
830
845
|
roles: principal.roles ?? [],
|
|
831
|
-
catalogue:
|
|
832
|
-
summaries:
|
|
846
|
+
catalogue: useService('roles')?.catalogue ?? {},
|
|
847
|
+
summaries: useService('roles')?.summaries ?? {},
|
|
833
848
|
})
|
|
834
849
|
const exp = principal.claims?.exp
|
|
835
850
|
if (!exp) {
|
|
@@ -1497,7 +1512,7 @@ function pinoLevelNumber(name) {
|
|
|
1497
1512
|
//
|
|
1498
1513
|
// The factory creates the substrate SYNCHRONOUSLY so other plugins
|
|
1499
1514
|
// listed AFTER 'mcp' in the array can register tools/resources at
|
|
1500
|
-
// their own onLoaded hook
|
|
1515
|
+
// their own onLoaded hook by asking core for the 'mcp' service
|
|
1501
1516
|
// gating already in place from the in-core era. The plugin MUST be
|
|
1502
1517
|
// FIRST in the user's plugins array — that's a documented invariant
|
|
1503
1518
|
// in this repo's README.
|
|
@@ -1539,12 +1554,19 @@ export function mcp(options = {}) {
|
|
|
1539
1554
|
add(runtime.options.corsExposeHeaders, ['mcp-session-id', 'mcp-protocol-version'])
|
|
1540
1555
|
}
|
|
1541
1556
|
|
|
1542
|
-
//
|
|
1543
|
-
//
|
|
1544
|
-
//
|
|
1545
|
-
//
|
|
1546
|
-
//
|
|
1547
|
-
|
|
1557
|
+
// Offered as a service rather than assigned to runtime.options.mcp.
|
|
1558
|
+
//
|
|
1559
|
+
// Tools do not need this at all any more — a plugin registers those
|
|
1560
|
+
// against the engine's registry and this package binds them into each
|
|
1561
|
+
// session. What is left is genuinely MCP's: resources and prompts, which
|
|
1562
|
+
// core deliberately has no vocabulary for.
|
|
1563
|
+
//
|
|
1564
|
+
// A consumer asks core for it from a hook, by which time every factory
|
|
1565
|
+
// has run — which retires the rule this comment used to carry, that the
|
|
1566
|
+
// mcp plugin MUST be FIRST in the user's plugins array. Nothing enforced
|
|
1567
|
+
// that; put it second and the tools silently vanished.
|
|
1568
|
+
const substrate = createMcpSubstrate()
|
|
1569
|
+
provideService('mcp', substrate, { plugin: 'mikser-io-mcp' })
|
|
1548
1570
|
// mikser_build_report needs the cycle recorded, and recording is off
|
|
1549
1571
|
// unless a reader asks for it — see report.js requestReport.
|
|
1550
1572
|
requestReport()
|
|
@@ -1565,7 +1587,7 @@ export function mcp(options = {}) {
|
|
|
1565
1587
|
// reference (held by plugins, render workers, useLogger
|
|
1566
1588
|
// consumers) gains the side-channel automatically.
|
|
1567
1589
|
if (runtime.engine?.logger) {
|
|
1568
|
-
wireLoggerToMcp(runtime.engine.logger,
|
|
1590
|
+
wireLoggerToMcp(runtime.engine.logger, substrate)
|
|
1569
1591
|
logger.debug('MCP logger wiring active')
|
|
1570
1592
|
}
|
|
1571
1593
|
|
|
@@ -1575,7 +1597,7 @@ export function mcp(options = {}) {
|
|
|
1575
1597
|
if (runtime.options.app && runtime.options.mcpPath) {
|
|
1576
1598
|
await mountMcpOnExpress(
|
|
1577
1599
|
runtime.options.app,
|
|
1578
|
-
|
|
1600
|
+
substrate,
|
|
1579
1601
|
runtime.options.mcpPath,
|
|
1580
1602
|
)
|
|
1581
1603
|
}
|
|
@@ -1590,7 +1612,7 @@ export function mcp(options = {}) {
|
|
|
1590
1612
|
// are silently skipped.
|
|
1591
1613
|
onLoaded(() => {
|
|
1592
1614
|
const logger = useLogger()
|
|
1593
|
-
const mcp =
|
|
1615
|
+
const mcp = substrate
|
|
1594
1616
|
if (!runtime.refs) {
|
|
1595
1617
|
logger.debug('mikser_refs_* tools skipped: runtime.refs not initialized')
|
|
1596
1618
|
return
|
|
@@ -1995,7 +2017,7 @@ export function mcp(options = {}) {
|
|
|
1995
2017
|
})
|
|
1996
2018
|
|
|
1997
2019
|
// mikser_layouts_inspect moved out — registered by mikser-io-layouts
|
|
1998
|
-
// directly against
|
|
2020
|
+
// directly against the substrate, same pattern vector and
|
|
1999
2021
|
// schemas use. Domain-owned tools live with the domain plugin; mcp
|
|
2000
2022
|
// is registry + transport, not a tool catalog.
|
|
2001
2023
|
|
|
@@ -2017,7 +2039,7 @@ export function mcp(options = {}) {
|
|
|
2017
2039
|
//
|
|
2018
2040
|
onLoaded(() => {
|
|
2019
2041
|
const logger = useLogger()
|
|
2020
|
-
const mcp =
|
|
2042
|
+
const mcp = substrate
|
|
2021
2043
|
|
|
2022
2044
|
const { render: mcpRender } = useRenderer(runtime, {
|
|
2023
2045
|
defaultTimeout: options.renderTimeout ?? 30_000,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mikser-io-mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "MCP (Model Context Protocol) substrate and tools for mikser-io. Extracted from core to iterate on its own release cadence.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"minimatch": "^10.0.3"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"mikser-io": "^
|
|
45
|
+
"mikser-io": "^10.0.0",
|
|
46
46
|
"zod": "^4.0.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
package/preview.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// the in-memory preview cache.
|
|
8
8
|
//
|
|
9
9
|
// The cache itself lives in mikser-io's preview plugin
|
|
10
|
-
// (
|
|
10
|
+
// (the `preview` service: {store, get, stats, config}). This module
|
|
11
11
|
// reaches into it via that surface — no cross-plugin imports.
|
|
12
12
|
|
|
13
13
|
import path from 'node:path'
|
|
@@ -15,7 +15,7 @@ import { readFileSync } from 'node:fs'
|
|
|
15
15
|
import { fileURLToPath } from 'node:url'
|
|
16
16
|
import { randomUUID, createHmac } from 'node:crypto'
|
|
17
17
|
import { z } from 'zod'
|
|
18
|
-
import { useRenderer, mimeForEntity, matchEntity } from 'mikser-io'
|
|
18
|
+
import { useRenderer, mimeForEntity, matchEntity, useService } from 'mikser-io'
|
|
19
19
|
|
|
20
20
|
// Resolve a path relative to this module's file. Used to find the
|
|
21
21
|
// public/ folder regardless of where the package is installed (works
|
|
@@ -122,7 +122,7 @@ const PREVIEW_UI_SHELL_HTML = readFileSync(path.join(__dirname, "public", "previ
|
|
|
122
122
|
// Plugin function — invoked by mikser-io-mcp/index.js's factory after
|
|
123
123
|
// the substrate is set up. Registers the MCP-UI surface (shell + modes
|
|
124
124
|
// resource + preview_ui + ui_action) AND the preview-render tool (which
|
|
125
|
-
//
|
|
125
|
+
// asks core for the preview service).
|
|
126
126
|
//
|
|
127
127
|
// Not a default export plugin in the mikser sense — this is internal
|
|
128
128
|
// composition. The mcp plugin's index.js is what mikser loads; this
|
|
@@ -135,15 +135,17 @@ export default ({
|
|
|
135
135
|
findEntities,
|
|
136
136
|
}) => {
|
|
137
137
|
onLoaded(() => {
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
// Asked for from a hook, so every factory has run — including this
|
|
139
|
+
// package's own, which provides it.
|
|
140
|
+
const mcp = useService('mcp')
|
|
141
|
+
if (!mcp) return
|
|
140
142
|
const { render: previewRender } = useRenderer(runtime, {
|
|
141
143
|
defaultTimeout: runtime.config.preview?.renderTimeout ?? 30_000,
|
|
142
144
|
})
|
|
143
145
|
|
|
144
146
|
// mikser_preview_render — render an entity through the pipeline,
|
|
145
147
|
// stash the bytes in the in-memory preview cache (provided by
|
|
146
|
-
// mikser-io core's preview plugin
|
|
148
|
+
// mikser-io core's preview plugin, offered as the `preview` service),
|
|
147
149
|
// and return a clickable URL. Requires --server (or any
|
|
148
150
|
// engine-supplied Express app) so the URL is reachable.
|
|
149
151
|
mcp.simpleTool(
|
|
@@ -155,7 +157,7 @@ export default ({
|
|
|
155
157
|
},
|
|
156
158
|
async ({ entity = {}, options = {} }) => {
|
|
157
159
|
const logger = useLogger()
|
|
158
|
-
const preview =
|
|
160
|
+
const preview = useService('preview')
|
|
159
161
|
const ok = (data) => ({
|
|
160
162
|
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
161
163
|
})
|
|
@@ -176,7 +178,7 @@ export default ({
|
|
|
176
178
|
return fail('mikser_preview_render requires either --url <public-url> or --server to be set so the preview URL is reachable. Use mikser_render to get raw bytes inline instead.')
|
|
177
179
|
}
|
|
178
180
|
if (!preview) {
|
|
179
|
-
return fail('mikser_preview_render requires the preview cache. Ensure mikser-io core is
|
|
181
|
+
return fail('mikser_preview_render requires the preview cache. Ensure the preview plugin from mikser-io core is in your plugins array.')
|
|
180
182
|
}
|
|
181
183
|
|
|
182
184
|
const cfg = preview.config()
|