icode-mcp-adapter 1.0.10 → 1.0.12
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "icode-mcp-adapter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "Dynamic MCP server adapter — auto-generates CRUD tools from schema configs. Plugs into icode-server via Fastify or runs standalone.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/engine/McpEngine.js",
|
package/src/engine/McpEngine.js
CHANGED
|
@@ -12,9 +12,9 @@ import { resolveSchemas as defaultResolveSchemas } from './SchemaAdapter.js';
|
|
|
12
12
|
*/
|
|
13
13
|
export function createMcpServer(config, db, identity, newGuid, writer) {
|
|
14
14
|
const server = new McpServer({
|
|
15
|
-
name: `${config.appName}-mcp`,
|
|
15
|
+
name: config.serverName || `${config.appName}-mcp`,
|
|
16
16
|
version: config.version,
|
|
17
|
-
});
|
|
17
|
+
}, config.instructions ? { instructions: config.instructions } : undefined);
|
|
18
18
|
|
|
19
19
|
for (const table of config.tables) {
|
|
20
20
|
registerTableTools(server, db, table, identity, newGuid, writer);
|
|
@@ -39,13 +39,15 @@ export async function createMcpServerAuto(config, db, opts = {}) {
|
|
|
39
39
|
const resolveSchemas = opts.resolveSchemas || defaultResolveSchemas;
|
|
40
40
|
|
|
41
41
|
const tables = await resolveSchemas(db, config.tables, {
|
|
42
|
-
cacheKey: `${config.appName}--${config.env || 'prod'}`,
|
|
42
|
+
cacheKey: config.cacheKey || `${config.appName}--${config.env || 'prod'}`,
|
|
43
43
|
ownerColumn: config.ownerColumn,
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
return createMcpServer({
|
|
47
47
|
appName: config.appName,
|
|
48
|
+
serverName: config.serverName,
|
|
48
49
|
version: config.version || '1.0.0',
|
|
50
|
+
instructions: config.instructions,
|
|
49
51
|
tables,
|
|
50
52
|
}, db, opts.identity, opts.newGuid, opts.writer);
|
|
51
53
|
}
|
|
@@ -45,6 +45,13 @@ const META_FIELDS = new Set([
|
|
|
45
45
|
'_by', // who last modified
|
|
46
46
|
'_deleted', // soft delete flag
|
|
47
47
|
'pk', // auto-increment secondary key
|
|
48
|
+
'_tenantId', // tenant marker (stamped by the host writer)
|
|
49
|
+
'_tzo', // timezone offset
|
|
50
|
+
'_vno', // version number
|
|
51
|
+
'_tag', // internal tagging
|
|
52
|
+
'org', // org partition
|
|
53
|
+
'settings', // per-row system settings json
|
|
54
|
+
'entitytype', // entity type marker (system-set)
|
|
48
55
|
]);
|
|
49
56
|
|
|
50
57
|
// ── Schema cache ─────────────────────────────────────────────────────────────
|
package/src/engine/types.js
CHANGED
|
@@ -40,9 +40,20 @@
|
|
|
40
40
|
* @property {string} name - App identifier (e.g., 'paaal')
|
|
41
41
|
* @property {string} displayName - Human-readable (e.g., 'PAAAL Coach')
|
|
42
42
|
* @property {string} version
|
|
43
|
+
* @property {string} [instructions] - Agent-facing usage guidance, sent to clients in the MCP initialize response
|
|
43
44
|
* @property {DbConfig} db
|
|
44
45
|
* @property {string} [ownerColumn] - App-wide default owner column for row-level scoping (per-table `ownerColumn` overrides)
|
|
45
46
|
* @property {TableSchema[]} tables
|
|
47
|
+
* @property {Record<string, SubServer>} [servers] - Optional grouped views over the app.
|
|
48
|
+
* The root URL stays the INDEX server (all app-level tables + instructions);
|
|
49
|
+
* each key is additionally served at /:appName/<key> as its own MCP server,
|
|
50
|
+
* inheriting from the app config and overriding what it declares.
|
|
51
|
+
*
|
|
52
|
+
* @typedef {Object} SubServer
|
|
53
|
+
* @property {string} [instructions] - Overrides the app-level instructions for this sub-server
|
|
54
|
+
* @property {Object} [tables] - Overrides the app-level tables (same shape; a table may
|
|
55
|
+
* appear in several sub-servers with different operations). Omit to inherit all.
|
|
56
|
+
* @property {string|null} [ownerColumn] - Overrides the app-level owner column
|
|
46
57
|
*/
|
|
47
58
|
|
|
48
59
|
export {};
|
|
@@ -13,13 +13,18 @@
|
|
|
13
13
|
* });
|
|
14
14
|
*
|
|
15
15
|
* Route param format:
|
|
16
|
-
* /v1/mcp/paaal--dev
|
|
17
|
-
* /v1/mcp/paaal
|
|
16
|
+
* /v1/mcp/paaal--dev → appName: 'paaal', env: 'dev'
|
|
17
|
+
* /v1/mcp/paaal → appName: 'paaal', env: 'prod' (default)
|
|
18
|
+
* /v1/mcp/paaal--dev/manage → sub-server 'manage' (when the config defines servers.manage)
|
|
18
19
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* The root URL is always the app's INDEX server (all app-level tables + instructions).
|
|
21
|
+
* Sub-servers are optional grouped views over it — each inherits from the app config
|
|
22
|
+
* and overrides what it declares (tables, instructions, ownerColumn).
|
|
23
|
+
*
|
|
24
|
+
* Routes (the /:sub variants serve apps whose config defines `servers`):
|
|
25
|
+
* POST /v1/mcp/:appName[/:sub] — MCP protocol endpoint
|
|
26
|
+
* DELETE /v1/mcp/:appName[/:sub] — Session termination
|
|
27
|
+
* GET /v1/mcp/:appName[/:sub]/health — Health check
|
|
23
28
|
*/
|
|
24
29
|
|
|
25
30
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
@@ -104,22 +109,37 @@ export default async function mcpPlugin(fastify, opts = {}) {
|
|
|
104
109
|
}
|
|
105
110
|
}
|
|
106
111
|
|
|
107
|
-
// Build lookup: key = "appName--env" (or "appName--prod" for default)
|
|
112
|
+
// Build lookup: key = "appName--env" (or "appName--prod" for default).
|
|
113
|
+
// The root key is the app's INDEX server — all app-level tables + instructions.
|
|
114
|
+
// Each `servers` entry is additionally served at "appName--env/sub" as its own
|
|
115
|
+
// MCP server, inheriting from the app config and overriding what it declares
|
|
116
|
+
// (tables, instructions, ownerColumn) — grouped tools per agent.
|
|
108
117
|
const apps = new Map();
|
|
109
118
|
for (const config of Object.values(opts.apps || {})) {
|
|
110
|
-
const
|
|
111
|
-
apps.set(
|
|
119
|
+
const base = `${config.appName}--${config.env || 'prod'}`;
|
|
120
|
+
apps.set(base, config);
|
|
121
|
+
for (const [sub, server] of Object.entries(config.servers || {})) {
|
|
122
|
+
apps.set(`${base}/${sub}`, {
|
|
123
|
+
appName: config.appName,
|
|
124
|
+
env: config.env,
|
|
125
|
+
version: config.version,
|
|
126
|
+
ownerColumn: server.ownerColumn !== undefined ? server.ownerColumn : config.ownerColumn,
|
|
127
|
+
instructions: server.instructions || config.instructions,
|
|
128
|
+
tables: server.tables || config.tables,
|
|
129
|
+
serverName: `${config.appName}-${sub}-mcp`,
|
|
130
|
+
cacheKey: `${base}--${sub}`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
112
133
|
}
|
|
113
134
|
if (!apps.size) throw new Error('icode-mcp-adapter: opts.apps is required (at least one app)');
|
|
114
135
|
|
|
115
136
|
// ── POST — MCP protocol endpoint ───────────────────────────────────────
|
|
116
|
-
|
|
117
|
-
config: { public: isPublic },
|
|
118
|
-
}, async (req, reply) => {
|
|
137
|
+
async function handleMcpPost(req, reply) {
|
|
119
138
|
const { appName, env } = parseAppParam(req.params.appName);
|
|
120
|
-
const
|
|
139
|
+
const key = req.params.sub ? `${appName}--${env}/${req.params.sub}` : `${appName}--${env}`;
|
|
140
|
+
const appConfig = apps.get(key);
|
|
121
141
|
if (!appConfig) {
|
|
122
|
-
return reply.code(404).send({ ok: false, error: `App '${
|
|
142
|
+
return reply.code(404).send({ ok: false, error: `App '${key}' not registered` });
|
|
123
143
|
}
|
|
124
144
|
|
|
125
145
|
const sessionId = req.headers['mcp-session-id'];
|
|
@@ -176,19 +196,23 @@ export default async function mcpPlugin(fastify, opts = {}) {
|
|
|
176
196
|
error: { code: -32000, message: 'Invalid session or missing initialization' },
|
|
177
197
|
id: null,
|
|
178
198
|
});
|
|
179
|
-
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
fastify.post(`${prefix}/:appName`, { config: { public: isPublic } }, handleMcpPost);
|
|
202
|
+
fastify.post(`${prefix}/:appName/:sub`, { config: { public: isPublic } }, handleMcpPost);
|
|
180
203
|
|
|
181
204
|
// ── DELETE — Session termination ───────────────────────────────────────
|
|
182
|
-
|
|
183
|
-
config: { public: isPublic },
|
|
184
|
-
}, async (req, reply) => {
|
|
205
|
+
async function handleMcpDelete(req, reply) {
|
|
185
206
|
const sessionId = req.headers['mcp-session-id'];
|
|
186
207
|
if (sessionId && sessions.has(sessionId)) {
|
|
187
208
|
await sessions.get(sessionId).transport.close();
|
|
188
209
|
sessions.delete(sessionId);
|
|
189
210
|
}
|
|
190
211
|
return reply.code(200).send();
|
|
191
|
-
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
fastify.delete(`${prefix}/:appName`, { config: { public: isPublic } }, handleMcpDelete);
|
|
215
|
+
fastify.delete(`${prefix}/:appName/:sub`, { config: { public: isPublic } }, handleMcpDelete);
|
|
192
216
|
|
|
193
217
|
// ── GET — Health check ─────────────────────────────────────────────────
|
|
194
218
|
fastify.get(`${prefix}/:appName/health`, {
|
|
@@ -197,6 +221,18 @@ export default async function mcpPlugin(fastify, opts = {}) {
|
|
|
197
221
|
const { appName, env } = parseAppParam(req.params.appName);
|
|
198
222
|
const appConfig = apps.get(`${appName}--${env}`);
|
|
199
223
|
if (!appConfig) return { ok: false, error: `App '${appName}/${env}' not registered` };
|
|
200
|
-
|
|
224
|
+
const out = { ok: true, app: appName, env, tables: Object.keys(appConfig.tables) };
|
|
225
|
+
if (appConfig.servers) out.servers = Object.keys(appConfig.servers);
|
|
226
|
+
return out;
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
fastify.get(`${prefix}/:appName/:sub/health`, {
|
|
230
|
+
config: { public: isPublic },
|
|
231
|
+
}, async (req) => {
|
|
232
|
+
const { appName, env } = parseAppParam(req.params.appName);
|
|
233
|
+
const key = `${appName}--${env}/${req.params.sub}`;
|
|
234
|
+
const appConfig = apps.get(key);
|
|
235
|
+
if (!appConfig) return { ok: false, error: `App '${key}' not registered` };
|
|
236
|
+
return { ok: true, app: appName, env, server: req.params.sub, tables: Object.keys(appConfig.tables) };
|
|
201
237
|
});
|
|
202
238
|
}
|