@leanmcp/core 0.3.11 → 0.3.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/README.md CHANGED
@@ -36,6 +36,7 @@
36
36
  - **Schema Generation** — Declarative JSON Schema with `@SchemaConstraint` decorators
37
37
  - **HTTP Transport** — Production-ready HTTP server with session management
38
38
  - **Input Validation** — Built-in AJV validation for all inputs
39
+ - **Structured Content** — Automatic `structuredContent` for ChatGPT Apps SDK compatibility
39
40
  - **MCP Compliant** — Built on official `@modelcontextprotocol/sdk`
40
41
 
41
42
  ## Installation
@@ -358,6 +359,42 @@ export class SlackService {
358
359
 
359
360
  ---
360
361
 
362
+ ## Structured Content
363
+
364
+ Tool return values are automatically exposed as `structuredContent` in the MCP response, enabling ChatGPT Apps SDK compatibility.
365
+
366
+ **Automatic Handling:**
367
+
368
+ ```typescript
369
+ @Tool({ description: 'List channels' })
370
+ async listChannels() {
371
+ // Return a plain object - it becomes structuredContent automatically
372
+ return { channels: [...] };
373
+ }
374
+ ```
375
+
376
+ The response includes both `content` (text) and `structuredContent` (object):
377
+
378
+ ```json
379
+ {
380
+ "content": [{ "type": "text", "text": "{\"channels\": [...]}" }],
381
+ "structuredContent": { "channels": [...] }
382
+ }
383
+ ```
384
+
385
+ **Manual MCP Response:**
386
+
387
+ If your tool returns a manual MCP response (with `content` array), the SDK extracts data from `content[0].text`:
388
+
389
+ ```typescript
390
+ return {
391
+ content: [{ type: 'text', text: JSON.stringify({ channels }) }]
392
+ };
393
+ // structuredContent will be { channels: [...] }
394
+ ```
395
+
396
+ ---
397
+
361
398
  ## HTTP Endpoints
362
399
 
363
400
  | Endpoint | Method | Description |
package/dist/index.js CHANGED
@@ -1161,10 +1161,27 @@ var init_index = __esm({
1161
1161
  const meta = request.params._meta;
1162
1162
  const result = await tool.method.call(tool.instance, request.params.arguments, meta);
1163
1163
  let formattedResult = result;
1164
+ let structuredContent = void 0;
1164
1165
  if (methodMeta.renderFormat === "markdown" && typeof result === "string") {
1165
1166
  formattedResult = result;
1166
- } else if (methodMeta.renderFormat === "json" || typeof result === "object") {
1167
- formattedResult = JSON.stringify(result, null, 2);
1167
+ } else if (typeof result === "object" && result !== null) {
1168
+ if ("structuredContent" in result && Object.keys(result).length === 1) {
1169
+ structuredContent = result.structuredContent;
1170
+ formattedResult = JSON.stringify(structuredContent, null, 2);
1171
+ } else if ("content" in result && Array.isArray(result.content)) {
1172
+ const textItem = result.content.find((c) => c.type === "text");
1173
+ if (textItem?.text) {
1174
+ try {
1175
+ structuredContent = JSON.parse(textItem.text);
1176
+ } catch {
1177
+ structuredContent = textItem.text;
1178
+ }
1179
+ }
1180
+ formattedResult = JSON.stringify(result, null, 2);
1181
+ } else {
1182
+ structuredContent = result;
1183
+ formattedResult = JSON.stringify(result, null, 2);
1184
+ }
1168
1185
  } else {
1169
1186
  formattedResult = String(result);
1170
1187
  }
@@ -1176,6 +1193,12 @@ var init_index = __esm({
1176
1193
  }
1177
1194
  ]
1178
1195
  };
1196
+ if (structuredContent) {
1197
+ response.structuredContent = structuredContent;
1198
+ if (this.logger) {
1199
+ this.logger.debug(`[MCPServer] Setting structuredContent: ${JSON.stringify(structuredContent).slice(0, 100)}...`);
1200
+ }
1201
+ }
1179
1202
  if (tool._meta && Object.keys(tool._meta).length > 0) {
1180
1203
  response._meta = tool._meta;
1181
1204
  }
package/dist/index.mjs CHANGED
@@ -1058,10 +1058,27 @@ var MCPServer = class {
1058
1058
  const meta = request.params._meta;
1059
1059
  const result = await tool.method.call(tool.instance, request.params.arguments, meta);
1060
1060
  let formattedResult = result;
1061
+ let structuredContent = void 0;
1061
1062
  if (methodMeta.renderFormat === "markdown" && typeof result === "string") {
1062
1063
  formattedResult = result;
1063
- } else if (methodMeta.renderFormat === "json" || typeof result === "object") {
1064
- formattedResult = JSON.stringify(result, null, 2);
1064
+ } else if (typeof result === "object" && result !== null) {
1065
+ if ("structuredContent" in result && Object.keys(result).length === 1) {
1066
+ structuredContent = result.structuredContent;
1067
+ formattedResult = JSON.stringify(structuredContent, null, 2);
1068
+ } else if ("content" in result && Array.isArray(result.content)) {
1069
+ const textItem = result.content.find((c) => c.type === "text");
1070
+ if (textItem?.text) {
1071
+ try {
1072
+ structuredContent = JSON.parse(textItem.text);
1073
+ } catch {
1074
+ structuredContent = textItem.text;
1075
+ }
1076
+ }
1077
+ formattedResult = JSON.stringify(result, null, 2);
1078
+ } else {
1079
+ structuredContent = result;
1080
+ formattedResult = JSON.stringify(result, null, 2);
1081
+ }
1065
1082
  } else {
1066
1083
  formattedResult = String(result);
1067
1084
  }
@@ -1073,6 +1090,12 @@ var MCPServer = class {
1073
1090
  }
1074
1091
  ]
1075
1092
  };
1093
+ if (structuredContent) {
1094
+ response.structuredContent = structuredContent;
1095
+ if (this.logger) {
1096
+ this.logger.debug(`[MCPServer] Setting structuredContent: ${JSON.stringify(structuredContent).slice(0, 100)}...`);
1097
+ }
1098
+ }
1076
1099
  if (tool._meta && Object.keys(tool._meta).length > 0) {
1077
1100
  response._meta = tool._meta;
1078
1101
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leanmcp/core",
3
- "version": "0.3.11",
3
+ "version": "0.3.12",
4
4
  "description": "Core library implementing decorators, reflection, and MCP runtime server",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",