feat(foundry): add composable subject detail profiles

This commit is contained in:
Codex
2026-07-22 19:06:33 +03:00
parent dd2febe7cf
commit dc82ae4c07
21 changed files with 1007 additions and 140 deletions
+88 -1
View File
@@ -303,6 +303,73 @@ const mapPresentationProfileInputSchema = {
},
};
const mapSubjectDetailProfileFieldInputSchema = {
type: "object",
additionalProperties: false,
required: ["id", "source", "field", "label", "format"],
properties: {
id: { type: "string", description: "Stable field presentation id." },
aspectId: { type: "string", description: "Provider-neutral subject aspect id; defaults to primary." },
source: { type: "string", enum: ["fact", "attribute", "geometry", "context"] },
field: { type: "string", description: "Exact registered fact, context, geometry or provider-neutral attribute field." },
label: { type: "string", minLength: 1, maxLength: 100 },
format: {
type: "string",
enum: ["text", "number", "timestamp", "boolean", "coordinate", "signal_state", "movement_state", "telemetry_readings"],
},
unit: { type: "string", minLength: 1, maxLength: 24 },
allowedReadingIds: {
type: "array",
maxItems: 256,
uniqueItems: true,
description: "Explicit classified sensor ids. Empty means no dynamic readings are rendered.",
items: { type: "string" },
},
},
};
const mapSubjectDetailProfileInputSchema = {
type: "object",
additionalProperties: false,
required: ["id", "version", "title", "semanticTypes", "defaultTabId", "tabs"],
properties: {
id: { type: "string", description: "Stable provider-neutral subject.detail_profile id." },
version: { type: "string", pattern: "^\\d+\\.\\d+\\.\\d+$" },
title: { type: "string", minLength: 1, maxLength: 120 },
semanticTypes: { type: "array", minItems: 1, maxItems: 8, uniqueItems: true, items: { type: "string" } },
defaultTabId: { type: "string" },
tabs: {
type: "array",
minItems: 1,
maxItems: 12,
items: {
type: "object",
additionalProperties: false,
required: ["id", "label", "emptyMessage", "sections"],
properties: {
id: { type: "string" },
label: { type: "string", minLength: 1, maxLength: 80 },
emptyMessage: { type: "string", minLength: 1, maxLength: 240 },
sections: {
type: "array",
maxItems: 12,
items: {
type: "object",
additionalProperties: false,
required: ["id", "label", "fields"],
properties: {
id: { type: "string" },
label: { type: "string", minLength: 1, maxLength: 80 },
fields: { type: "array", maxItems: 32, items: mapSubjectDetailProfileFieldInputSchema },
},
},
},
},
},
},
},
};
const mapPageSettingsPatchInputSchema = {
type: "object",
additionalProperties: false,
@@ -551,6 +618,22 @@ const tools = [
},
},
},
{
name: "foundry_upsert_map_subject_detail_profile",
title: "Upsert Map subject detail profile",
description: "Create or update a versioned provider-neutral subject detail profile. Tabs and fields are exact, fail-closed presentation declarations; provider transport and unrestricted payload rendering are forbidden.",
inputSchema: {
type: "object",
additionalProperties: false,
required: ["applicationId", "pageId", "idempotencyKey", "profile"],
properties: {
applicationId: { type: "string" },
pageId: { type: "string", description: "Map Page instance id inside the application." },
idempotencyKey: { type: "string" },
profile: mapSubjectDetailProfileInputSchema,
},
},
},
{
name: "foundry_update_map_page_settings",
title: "Update Application Map settings",
@@ -629,6 +712,9 @@ const tools = [
semanticTypes: { type: "array", items: { type: "string" } },
fieldProjection: { type: "array", items: { type: "string" } },
presentationProfileId: { type: "string", description: "Existing page-owned provider-neutral Map presentation profile id." },
subjectDetailProfileId: { type: "string", description: "Existing page-owned provider-neutral subject detail profile id." },
aspectId: { type: "string", description: "Stable provider-neutral aspect id inside the selected subject composition." },
joinToBindingId: { type: "string", description: "Primary Map binding whose stable sourceId is used to join this subject-details aspect." },
},
},
},
@@ -728,6 +814,7 @@ function toolMap(operations) {
foundry_upsert_map_pin_binding: (input, actor) => operations.upsertMapPinBinding(input, actor),
foundry_remove_map_pin_binding: (input, actor) => operations.removeMapPinBinding(input, actor),
foundry_upsert_map_presentation_profile: (input, actor) => operations.upsertMapPresentationProfile(input, actor),
foundry_upsert_map_subject_detail_profile: (input, actor) => operations.upsertMapSubjectDetailProfile(input, actor),
foundry_update_map_page_settings: (input, actor) => operations.updateMapPageSettings(input, actor),
foundry_save_map_page_view_state: (input, actor) => operations.saveMapPageViewState(input, actor),
foundry_upsert_map_data_product_binding: (input, actor) => operations.upsertMapDataProductBinding(input, actor),
@@ -772,7 +859,7 @@ export async function handleFoundryMcpRequest(request, response, options) {
return sendJson(response, 200, mcpResult(id, {
protocolVersion: MCP_PROTOCOL_VERSION,
capabilities: { tools: { listChanged: false } },
serverInfo: { name: "nodedc_module_foundry", version: "0.5.0" },
serverInfo: { name: "nodedc_module_foundry", version: "0.6.0" },
instructions: "NDC Module Foundry edits application instances, provider-neutral map presentation profiles and approved server-owned data-product consumers. Page Library is read-only. Application deletion is unavailable. Provider endpoints and credentials are never MCP inputs or outputs.",
}));
}