@oimdb/mcp 1.0.3 → 1.1.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/README.md +43 -0
- package/bin/server.cjs +3 -1
- package/dist/index.cjs +70 -10
- package/dist/index.d.cts +36 -1
- package/dist/index.d.ts +36 -1
- package/dist/index.js +70 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,6 +108,49 @@ Call oimdb_inspect to see what's in the store.
|
|
|
108
108
|
| `oimdb_collection` | Details of one collection by name |
|
|
109
109
|
| `oimdb_computed` | Status + deps of one computed by name |
|
|
110
110
|
|
|
111
|
+
## Offline / static mode (no browser)
|
|
112
|
+
|
|
113
|
+
By default the tools read a **live browser tab**. You can instead point the
|
|
114
|
+
server at an **in-process model** so it answers offline — ideal for "what's the
|
|
115
|
+
data model?" questions without running the app.
|
|
116
|
+
|
|
117
|
+
**CLI — a model module.** Set `OIMDB_MODEL_MODULE` to a module that builds the
|
|
118
|
+
model in Node and exports a `registry` (or a default export, or a zero-arg
|
|
119
|
+
factory returning one):
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
// oimdb-model.js (built JS, or run the server under a TS loader)
|
|
123
|
+
import { OIMEventQueue, OIMReactiveCollection } from '@oimdb/core';
|
|
124
|
+
import { OIMDevRegistry } from '@oimdb/devtools';
|
|
125
|
+
|
|
126
|
+
const queue = new OIMEventQueue();
|
|
127
|
+
const users = new OIMReactiveCollection(queue, { selectPk: u => u.id });
|
|
128
|
+
|
|
129
|
+
export const registry = new OIMDevRegistry();
|
|
130
|
+
registry.collection('users', users, { indexes: { /* ... */ } });
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
OIMDB_MODEL_MODULE=./oimdb-model.js npx @oimdb/mcp
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
No WebSocket port is opened in this mode — no browser required.
|
|
138
|
+
|
|
139
|
+
**Embedded — inject a registry.** When running the server from your own Node
|
|
140
|
+
process, pass the registry directly:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { createOIMDevMcpServer } from '@oimdb/mcp';
|
|
144
|
+
import { registry } from './oimdb-model';
|
|
145
|
+
|
|
146
|
+
await createOIMDevMcpServer({ registry }).start();
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
> **Fields offline:** structure (collections, indexes, relations) is available
|
|
150
|
+
> as soon as the model is constructed. Entity **field names** appear only when
|
|
151
|
+
> the model holds at least one sample entity (TypeScript types are erased at
|
|
152
|
+
> runtime) — seed a representative row in the model module if you want them.
|
|
153
|
+
|
|
111
154
|
## CDP Inspector (no browser-side code needed)
|
|
112
155
|
|
|
113
156
|
Launch Chrome with `--remote-debugging-port=9222`, then:
|
package/bin/server.cjs
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
const { OIMDevMcpServer } = require('../dist/index.cjs');
|
|
3
3
|
|
|
4
4
|
const wsPort = process.env.OIMDB_WS_PORT ? Number(process.env.OIMDB_WS_PORT) : 7432;
|
|
5
|
-
|
|
5
|
+
// Offline/static mode: point at a module that builds the model in-process.
|
|
6
|
+
const modelModulePath = process.env.OIMDB_MODEL_MODULE || undefined;
|
|
7
|
+
const server = new OIMDevMcpServer({ wsPort, modelModulePath });
|
|
6
8
|
server.start().catch((err) => {
|
|
7
9
|
process.stderr.write('[OIMDB MCP] Fatal: ' + err.message + '\n');
|
|
8
10
|
process.exit(1);
|
package/dist/index.cjs
CHANGED
|
@@ -31,36 +31,86 @@ module.exports = __toCommonJS(index_exports);
|
|
|
31
31
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
32
32
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
33
33
|
var import_ws = require("ws");
|
|
34
|
+
var import_node_url = require("url");
|
|
35
|
+
var import_node_path = require("path");
|
|
34
36
|
var import_zod = require("zod");
|
|
37
|
+
async function loadRegistryFromModule(path) {
|
|
38
|
+
const url = (0, import_node_url.pathToFileURL)((0, import_node_path.resolve)(path)).href;
|
|
39
|
+
const mod = await import(url);
|
|
40
|
+
let candidate = mod.registry ?? mod.default ?? mod;
|
|
41
|
+
if (typeof candidate === "function") {
|
|
42
|
+
candidate = await candidate();
|
|
43
|
+
}
|
|
44
|
+
if (!candidate || typeof candidate.inspect !== "function" || typeof candidate.dumpString !== "function") {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`[OIMDB MCP] Module "${path}" must export a registry exposing inspect() and dumpString() (as a "registry" or default export, or a zero-arg factory returning one).`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return candidate;
|
|
50
|
+
}
|
|
35
51
|
var OIMDevMcpServer = class {
|
|
36
52
|
constructor(options = {}) {
|
|
37
53
|
this.wss = null;
|
|
38
54
|
this.latestBrowser = null;
|
|
39
55
|
this.pending = /* @__PURE__ */ new Map();
|
|
56
|
+
this.modelModuleLoaded = false;
|
|
40
57
|
this.wsPort = options.wsPort ?? 7432;
|
|
41
|
-
this.
|
|
58
|
+
this.inProcessRegistry = options.registry ?? null;
|
|
59
|
+
this.modelModulePath = options.modelModulePath;
|
|
60
|
+
this.mcpServer = new import_mcp.McpServer(
|
|
61
|
+
{ name: "oimdb", version: "1.0.0" },
|
|
62
|
+
{
|
|
63
|
+
instructions: "Before using these tools, read the OIMDB API reference so you know how to write correct code: node_modules/@oimdb/core/llms.txt. Then use oimdb_inspect to see the data model (collections, indexes, computeds). The source is either a live browser tab (WebSocket bridge) or an in-process model (when a registry / model module is provided) for offline/static introspection."
|
|
64
|
+
}
|
|
65
|
+
);
|
|
42
66
|
this.registerTools();
|
|
43
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolves the in-process registry, loading the model module on first use.
|
|
70
|
+
* Returns null when no in-process source is configured (browser-bridge mode).
|
|
71
|
+
*/
|
|
72
|
+
async getInProcessRegistry() {
|
|
73
|
+
if (this.inProcessRegistry) return this.inProcessRegistry;
|
|
74
|
+
if (this.modelModulePath && !this.modelModuleLoaded) {
|
|
75
|
+
this.modelModuleLoaded = true;
|
|
76
|
+
this.inProcessRegistry = await loadRegistryFromModule(
|
|
77
|
+
this.modelModulePath
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return this.inProcessRegistry;
|
|
81
|
+
}
|
|
82
|
+
/** Reads the inspect snapshot from the in-process model or the browser. */
|
|
83
|
+
async readInspect() {
|
|
84
|
+
const registry = await this.getInProcessRegistry();
|
|
85
|
+
if (registry) return registry.inspect();
|
|
86
|
+
return this.queryBrowser("inspect");
|
|
87
|
+
}
|
|
88
|
+
/** Reads the human-readable dump from the in-process model or the browser. */
|
|
89
|
+
async readDump() {
|
|
90
|
+
const registry = await this.getInProcessRegistry();
|
|
91
|
+
if (registry) return registry.dumpString();
|
|
92
|
+
return await this.queryBrowser("dump");
|
|
93
|
+
}
|
|
44
94
|
registerTools() {
|
|
45
95
|
const server = this.mcpServer;
|
|
46
96
|
const nameSchema = { name: import_zod.z.string().describe("Registered name") };
|
|
47
97
|
server.registerTool("oimdb_inspect", {
|
|
48
|
-
description: "Get full state of all registered OIMDB collections and computeds
|
|
98
|
+
description: "Get full state of all registered OIMDB collections and computeds. Returns structured JSON with entity counts, field names, index keys, relations, and computed statuses. Source is the connected browser tab, or an in-process model when one is configured (offline/static \u2014 field names appear only if the model holds sample entities)."
|
|
49
99
|
}, async () => {
|
|
50
|
-
const data = await this.
|
|
100
|
+
const data = await this.readInspect();
|
|
51
101
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
52
102
|
});
|
|
53
103
|
server.registerTool("oimdb_dump", {
|
|
54
|
-
description: "Get a human-readable text summary of OIMDB state. Same output as registry.dump()
|
|
104
|
+
description: "Get a human-readable text summary of OIMDB state. Same output as registry.dump(). Reads the connected browser tab or an in-process model when configured."
|
|
55
105
|
}, async () => {
|
|
56
|
-
const text = await this.
|
|
106
|
+
const text = await this.readDump();
|
|
57
107
|
return { content: [{ type: "text", text }] };
|
|
58
108
|
});
|
|
59
109
|
server.registerTool("oimdb_collection", {
|
|
60
110
|
description: "Get details of a specific registered collection: entity count, field names, index keys, and relations.",
|
|
61
111
|
inputSchema: nameSchema
|
|
62
112
|
}, async ({ name }) => {
|
|
63
|
-
const data = await this.
|
|
113
|
+
const data = await this.readInspect();
|
|
64
114
|
const info = data.collections[name];
|
|
65
115
|
if (!info) {
|
|
66
116
|
const available = Object.keys(data.collections).join(", ") || "(none registered)";
|
|
@@ -72,7 +122,7 @@ var OIMDevMcpServer = class {
|
|
|
72
122
|
description: "Get status and resolved dependency names of a specific registered computed value. Shows fresh/stale/pending status, current value, and which registered sources it depends on.",
|
|
73
123
|
inputSchema: nameSchema
|
|
74
124
|
}, async ({ name }) => {
|
|
75
|
-
const data = await this.
|
|
125
|
+
const data = await this.readInspect();
|
|
76
126
|
const info = data.computeds[name];
|
|
77
127
|
if (!info) {
|
|
78
128
|
const available = Object.keys(data.computeds).join(", ") || "(none registered)";
|
|
@@ -86,11 +136,14 @@ var OIMDevMcpServer = class {
|
|
|
86
136
|
throw new Error(
|
|
87
137
|
`No browser tab connected to the OIMDB MCP bridge.
|
|
88
138
|
|
|
89
|
-
|
|
139
|
+
Live mode \u2014 add to your debug entry point:
|
|
90
140
|
import { registry } from '@oimdb/devtools';
|
|
91
141
|
registry.connect(); // connects to ws://localhost:${this.wsPort}
|
|
142
|
+
Then reload the page.
|
|
92
143
|
|
|
93
|
-
|
|
144
|
+
Offline mode \u2014 point the server at a model module instead:
|
|
145
|
+
OIMDB_MODEL_MODULE=./path/to/model.js npx @oimdb/mcp
|
|
146
|
+
(the module exports a "registry" with inspect()/dumpString()).`
|
|
94
147
|
);
|
|
95
148
|
}
|
|
96
149
|
return new Promise((resolve, reject) => {
|
|
@@ -150,7 +203,14 @@ Set a different port: OIMDB_WS_PORT=7433 npx @oimdb/mcp
|
|
|
150
203
|
`);
|
|
151
204
|
}
|
|
152
205
|
async start() {
|
|
153
|
-
this.
|
|
206
|
+
const registry = await this.getInProcessRegistry();
|
|
207
|
+
if (registry) {
|
|
208
|
+
process.stderr.write(
|
|
209
|
+
"[OIMDB MCP] In-process model loaded \u2014 serving offline (no browser bridge).\n"
|
|
210
|
+
);
|
|
211
|
+
} else {
|
|
212
|
+
this.setupWebSocketServer();
|
|
213
|
+
}
|
|
154
214
|
const transport = new import_stdio.StdioServerTransport();
|
|
155
215
|
await this.mcpServer.connect(transport);
|
|
156
216
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The readable shape the MCP tools need from a registry — `OIMDevRegistry` from
|
|
3
|
+
* `@oimdb/devtools` already satisfies it. Declared structurally (following the
|
|
4
|
+
* existing `IOIMDev*Like` convention) so `@oimdb/mcp` need not depend on
|
|
5
|
+
* `@oimdb/devtools`; it is not a separate registry implementation.
|
|
6
|
+
*/
|
|
7
|
+
interface IOIMDevRegistryLike {
|
|
8
|
+
inspect(): unknown;
|
|
9
|
+
dumpString(): string;
|
|
10
|
+
}
|
|
1
11
|
type TOIMDevMcpOptions = {
|
|
2
12
|
/** Port for the WebSocket bridge that browser tabs connect to. Default: 7432 */
|
|
3
13
|
wsPort?: number;
|
|
14
|
+
/**
|
|
15
|
+
* An in-process registry to read directly (your `OIMDevRegistry`). When
|
|
16
|
+
* provided, the tools answer from it without a browser/WebSocket bridge
|
|
17
|
+
* (no `wsPort` server started).
|
|
18
|
+
*/
|
|
19
|
+
registry?: IOIMDevRegistryLike;
|
|
20
|
+
/**
|
|
21
|
+
* Path to a module that builds the model in this process for offline
|
|
22
|
+
* introspection. The module must export a `registry` (or a default export,
|
|
23
|
+
* or a zero-arg factory) exposing `inspect()` and `dumpString()`.
|
|
24
|
+
* Also settable via the `OIMDB_MODEL_MODULE` env var for the CLI.
|
|
25
|
+
*/
|
|
26
|
+
modelModulePath?: string;
|
|
4
27
|
};
|
|
5
28
|
|
|
6
29
|
declare class OIMDevMcpServer {
|
|
@@ -9,7 +32,19 @@ declare class OIMDevMcpServer {
|
|
|
9
32
|
private wss;
|
|
10
33
|
private latestBrowser;
|
|
11
34
|
private readonly pending;
|
|
35
|
+
private inProcessRegistry;
|
|
36
|
+
private readonly modelModulePath?;
|
|
37
|
+
private modelModuleLoaded;
|
|
12
38
|
constructor(options?: TOIMDevMcpOptions);
|
|
39
|
+
/**
|
|
40
|
+
* Resolves the in-process registry, loading the model module on first use.
|
|
41
|
+
* Returns null when no in-process source is configured (browser-bridge mode).
|
|
42
|
+
*/
|
|
43
|
+
private getInProcessRegistry;
|
|
44
|
+
/** Reads the inspect snapshot from the in-process model or the browser. */
|
|
45
|
+
private readInspect;
|
|
46
|
+
/** Reads the human-readable dump from the in-process model or the browser. */
|
|
47
|
+
private readDump;
|
|
13
48
|
private registerTools;
|
|
14
49
|
private queryBrowser;
|
|
15
50
|
private setupWebSocketServer;
|
|
@@ -41,4 +76,4 @@ declare class OIMDevUIServer {
|
|
|
41
76
|
declare function createOIMDevMcpServer(options?: TOIMDevMcpOptions): OIMDevMcpServer;
|
|
42
77
|
declare function createOIMDevUIServer(options: TOIMDevUIServerOptions): OIMDevUIServer;
|
|
43
78
|
|
|
44
|
-
export { OIMDevMcpServer, OIMDevUIServer, type TOIMDevMcpOptions, type TOIMDevUIServerOptions, createOIMDevMcpServer, createOIMDevUIServer };
|
|
79
|
+
export { type IOIMDevRegistryLike, OIMDevMcpServer, OIMDevUIServer, type TOIMDevMcpOptions, type TOIMDevUIServerOptions, createOIMDevMcpServer, createOIMDevUIServer };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The readable shape the MCP tools need from a registry — `OIMDevRegistry` from
|
|
3
|
+
* `@oimdb/devtools` already satisfies it. Declared structurally (following the
|
|
4
|
+
* existing `IOIMDev*Like` convention) so `@oimdb/mcp` need not depend on
|
|
5
|
+
* `@oimdb/devtools`; it is not a separate registry implementation.
|
|
6
|
+
*/
|
|
7
|
+
interface IOIMDevRegistryLike {
|
|
8
|
+
inspect(): unknown;
|
|
9
|
+
dumpString(): string;
|
|
10
|
+
}
|
|
1
11
|
type TOIMDevMcpOptions = {
|
|
2
12
|
/** Port for the WebSocket bridge that browser tabs connect to. Default: 7432 */
|
|
3
13
|
wsPort?: number;
|
|
14
|
+
/**
|
|
15
|
+
* An in-process registry to read directly (your `OIMDevRegistry`). When
|
|
16
|
+
* provided, the tools answer from it without a browser/WebSocket bridge
|
|
17
|
+
* (no `wsPort` server started).
|
|
18
|
+
*/
|
|
19
|
+
registry?: IOIMDevRegistryLike;
|
|
20
|
+
/**
|
|
21
|
+
* Path to a module that builds the model in this process for offline
|
|
22
|
+
* introspection. The module must export a `registry` (or a default export,
|
|
23
|
+
* or a zero-arg factory) exposing `inspect()` and `dumpString()`.
|
|
24
|
+
* Also settable via the `OIMDB_MODEL_MODULE` env var for the CLI.
|
|
25
|
+
*/
|
|
26
|
+
modelModulePath?: string;
|
|
4
27
|
};
|
|
5
28
|
|
|
6
29
|
declare class OIMDevMcpServer {
|
|
@@ -9,7 +32,19 @@ declare class OIMDevMcpServer {
|
|
|
9
32
|
private wss;
|
|
10
33
|
private latestBrowser;
|
|
11
34
|
private readonly pending;
|
|
35
|
+
private inProcessRegistry;
|
|
36
|
+
private readonly modelModulePath?;
|
|
37
|
+
private modelModuleLoaded;
|
|
12
38
|
constructor(options?: TOIMDevMcpOptions);
|
|
39
|
+
/**
|
|
40
|
+
* Resolves the in-process registry, loading the model module on first use.
|
|
41
|
+
* Returns null when no in-process source is configured (browser-bridge mode).
|
|
42
|
+
*/
|
|
43
|
+
private getInProcessRegistry;
|
|
44
|
+
/** Reads the inspect snapshot from the in-process model or the browser. */
|
|
45
|
+
private readInspect;
|
|
46
|
+
/** Reads the human-readable dump from the in-process model or the browser. */
|
|
47
|
+
private readDump;
|
|
13
48
|
private registerTools;
|
|
14
49
|
private queryBrowser;
|
|
15
50
|
private setupWebSocketServer;
|
|
@@ -41,4 +76,4 @@ declare class OIMDevUIServer {
|
|
|
41
76
|
declare function createOIMDevMcpServer(options?: TOIMDevMcpOptions): OIMDevMcpServer;
|
|
42
77
|
declare function createOIMDevUIServer(options: TOIMDevUIServerOptions): OIMDevUIServer;
|
|
43
78
|
|
|
44
|
-
export { OIMDevMcpServer, OIMDevUIServer, type TOIMDevMcpOptions, type TOIMDevUIServerOptions, createOIMDevMcpServer, createOIMDevUIServer };
|
|
79
|
+
export { type IOIMDevRegistryLike, OIMDevMcpServer, OIMDevUIServer, type TOIMDevMcpOptions, type TOIMDevUIServerOptions, createOIMDevMcpServer, createOIMDevUIServer };
|
package/dist/index.js
CHANGED
|
@@ -2,36 +2,86 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { WebSocketServer } from "ws";
|
|
5
|
+
import { pathToFileURL } from "url";
|
|
6
|
+
import { resolve as resolvePath } from "path";
|
|
5
7
|
import { z } from "zod";
|
|
8
|
+
async function loadRegistryFromModule(path) {
|
|
9
|
+
const url = pathToFileURL(resolvePath(path)).href;
|
|
10
|
+
const mod = await import(url);
|
|
11
|
+
let candidate = mod.registry ?? mod.default ?? mod;
|
|
12
|
+
if (typeof candidate === "function") {
|
|
13
|
+
candidate = await candidate();
|
|
14
|
+
}
|
|
15
|
+
if (!candidate || typeof candidate.inspect !== "function" || typeof candidate.dumpString !== "function") {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`[OIMDB MCP] Module "${path}" must export a registry exposing inspect() and dumpString() (as a "registry" or default export, or a zero-arg factory returning one).`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return candidate;
|
|
21
|
+
}
|
|
6
22
|
var OIMDevMcpServer = class {
|
|
7
23
|
constructor(options = {}) {
|
|
8
24
|
this.wss = null;
|
|
9
25
|
this.latestBrowser = null;
|
|
10
26
|
this.pending = /* @__PURE__ */ new Map();
|
|
27
|
+
this.modelModuleLoaded = false;
|
|
11
28
|
this.wsPort = options.wsPort ?? 7432;
|
|
12
|
-
this.
|
|
29
|
+
this.inProcessRegistry = options.registry ?? null;
|
|
30
|
+
this.modelModulePath = options.modelModulePath;
|
|
31
|
+
this.mcpServer = new McpServer(
|
|
32
|
+
{ name: "oimdb", version: "1.0.0" },
|
|
33
|
+
{
|
|
34
|
+
instructions: "Before using these tools, read the OIMDB API reference so you know how to write correct code: node_modules/@oimdb/core/llms.txt. Then use oimdb_inspect to see the data model (collections, indexes, computeds). The source is either a live browser tab (WebSocket bridge) or an in-process model (when a registry / model module is provided) for offline/static introspection."
|
|
35
|
+
}
|
|
36
|
+
);
|
|
13
37
|
this.registerTools();
|
|
14
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolves the in-process registry, loading the model module on first use.
|
|
41
|
+
* Returns null when no in-process source is configured (browser-bridge mode).
|
|
42
|
+
*/
|
|
43
|
+
async getInProcessRegistry() {
|
|
44
|
+
if (this.inProcessRegistry) return this.inProcessRegistry;
|
|
45
|
+
if (this.modelModulePath && !this.modelModuleLoaded) {
|
|
46
|
+
this.modelModuleLoaded = true;
|
|
47
|
+
this.inProcessRegistry = await loadRegistryFromModule(
|
|
48
|
+
this.modelModulePath
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return this.inProcessRegistry;
|
|
52
|
+
}
|
|
53
|
+
/** Reads the inspect snapshot from the in-process model or the browser. */
|
|
54
|
+
async readInspect() {
|
|
55
|
+
const registry = await this.getInProcessRegistry();
|
|
56
|
+
if (registry) return registry.inspect();
|
|
57
|
+
return this.queryBrowser("inspect");
|
|
58
|
+
}
|
|
59
|
+
/** Reads the human-readable dump from the in-process model or the browser. */
|
|
60
|
+
async readDump() {
|
|
61
|
+
const registry = await this.getInProcessRegistry();
|
|
62
|
+
if (registry) return registry.dumpString();
|
|
63
|
+
return await this.queryBrowser("dump");
|
|
64
|
+
}
|
|
15
65
|
registerTools() {
|
|
16
66
|
const server = this.mcpServer;
|
|
17
67
|
const nameSchema = { name: z.string().describe("Registered name") };
|
|
18
68
|
server.registerTool("oimdb_inspect", {
|
|
19
|
-
description: "Get full state of all registered OIMDB collections and computeds
|
|
69
|
+
description: "Get full state of all registered OIMDB collections and computeds. Returns structured JSON with entity counts, field names, index keys, relations, and computed statuses. Source is the connected browser tab, or an in-process model when one is configured (offline/static \u2014 field names appear only if the model holds sample entities)."
|
|
20
70
|
}, async () => {
|
|
21
|
-
const data = await this.
|
|
71
|
+
const data = await this.readInspect();
|
|
22
72
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
23
73
|
});
|
|
24
74
|
server.registerTool("oimdb_dump", {
|
|
25
|
-
description: "Get a human-readable text summary of OIMDB state. Same output as registry.dump()
|
|
75
|
+
description: "Get a human-readable text summary of OIMDB state. Same output as registry.dump(). Reads the connected browser tab or an in-process model when configured."
|
|
26
76
|
}, async () => {
|
|
27
|
-
const text = await this.
|
|
77
|
+
const text = await this.readDump();
|
|
28
78
|
return { content: [{ type: "text", text }] };
|
|
29
79
|
});
|
|
30
80
|
server.registerTool("oimdb_collection", {
|
|
31
81
|
description: "Get details of a specific registered collection: entity count, field names, index keys, and relations.",
|
|
32
82
|
inputSchema: nameSchema
|
|
33
83
|
}, async ({ name }) => {
|
|
34
|
-
const data = await this.
|
|
84
|
+
const data = await this.readInspect();
|
|
35
85
|
const info = data.collections[name];
|
|
36
86
|
if (!info) {
|
|
37
87
|
const available = Object.keys(data.collections).join(", ") || "(none registered)";
|
|
@@ -43,7 +93,7 @@ var OIMDevMcpServer = class {
|
|
|
43
93
|
description: "Get status and resolved dependency names of a specific registered computed value. Shows fresh/stale/pending status, current value, and which registered sources it depends on.",
|
|
44
94
|
inputSchema: nameSchema
|
|
45
95
|
}, async ({ name }) => {
|
|
46
|
-
const data = await this.
|
|
96
|
+
const data = await this.readInspect();
|
|
47
97
|
const info = data.computeds[name];
|
|
48
98
|
if (!info) {
|
|
49
99
|
const available = Object.keys(data.computeds).join(", ") || "(none registered)";
|
|
@@ -57,11 +107,14 @@ var OIMDevMcpServer = class {
|
|
|
57
107
|
throw new Error(
|
|
58
108
|
`No browser tab connected to the OIMDB MCP bridge.
|
|
59
109
|
|
|
60
|
-
|
|
110
|
+
Live mode \u2014 add to your debug entry point:
|
|
61
111
|
import { registry } from '@oimdb/devtools';
|
|
62
112
|
registry.connect(); // connects to ws://localhost:${this.wsPort}
|
|
113
|
+
Then reload the page.
|
|
63
114
|
|
|
64
|
-
|
|
115
|
+
Offline mode \u2014 point the server at a model module instead:
|
|
116
|
+
OIMDB_MODEL_MODULE=./path/to/model.js npx @oimdb/mcp
|
|
117
|
+
(the module exports a "registry" with inspect()/dumpString()).`
|
|
65
118
|
);
|
|
66
119
|
}
|
|
67
120
|
return new Promise((resolve, reject) => {
|
|
@@ -121,7 +174,14 @@ Set a different port: OIMDB_WS_PORT=7433 npx @oimdb/mcp
|
|
|
121
174
|
`);
|
|
122
175
|
}
|
|
123
176
|
async start() {
|
|
124
|
-
this.
|
|
177
|
+
const registry = await this.getInProcessRegistry();
|
|
178
|
+
if (registry) {
|
|
179
|
+
process.stderr.write(
|
|
180
|
+
"[OIMDB MCP] In-process model loaded \u2014 serving offline (no browser bridge).\n"
|
|
181
|
+
);
|
|
182
|
+
} else {
|
|
183
|
+
this.setupWebSocketServer();
|
|
184
|
+
}
|
|
125
185
|
const transport = new StdioServerTransport();
|
|
126
186
|
await this.mcpServer.connect(transport);
|
|
127
187
|
}
|