diff --git a/examples/basic-server-react/server.ts b/examples/basic-server-react/server.ts index feebf23b..3f6d6d34 100644 --- a/examples/basic-server-react/server.ts +++ b/examples/basic-server-react/server.ts @@ -5,7 +5,6 @@ import cors from "cors"; import express, { type Request, type Response } from "express"; import fs from "node:fs/promises"; import path from "node:path"; -import { z } from "zod"; import { RESOURCE_URI_META_KEY } from "../../dist/src/app"; const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 3001; @@ -30,14 +29,12 @@ const server = new McpServer({ title: "Get Time", description: "Returns the current server time as an ISO 8601 string.", inputSchema: {}, - outputSchema: { time: z.string() }, _meta: { [RESOURCE_URI_META_KEY]: resourceUri }, }, async (): Promise => { const time = new Date().toISOString(); return { - content: [{ type: "text", text: time }], - structuredContent: { time }, + content: [{ type: "text", text: JSON.stringify({ time }) }], }; }, ); diff --git a/examples/basic-server-react/src/mcp-app.tsx b/examples/basic-server-react/src/mcp-app.tsx index c3163fd1..eb5bb131 100644 --- a/examples/basic-server-react/src/mcp-app.tsx +++ b/examples/basic-server-react/src/mcp-app.tsx @@ -20,8 +20,12 @@ const log = { function extractTime(callToolResult: CallToolResult): string { - const { time } = (callToolResult.structuredContent as { time?: string }) ?? {}; - return time ?? "[ERROR]"; + const text = callToolResult.content! + .filter((c): c is { type: "text"; text: string } => c.type === "text") + .map((c) => c.text) + .join(""); + const { time } = JSON.parse(text) as { time: string }; + return time; } diff --git a/examples/basic-server-vanillajs/server.ts b/examples/basic-server-vanillajs/server.ts index feebf23b..3f6d6d34 100644 --- a/examples/basic-server-vanillajs/server.ts +++ b/examples/basic-server-vanillajs/server.ts @@ -5,7 +5,6 @@ import cors from "cors"; import express, { type Request, type Response } from "express"; import fs from "node:fs/promises"; import path from "node:path"; -import { z } from "zod"; import { RESOURCE_URI_META_KEY } from "../../dist/src/app"; const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 3001; @@ -30,14 +29,12 @@ const server = new McpServer({ title: "Get Time", description: "Returns the current server time as an ISO 8601 string.", inputSchema: {}, - outputSchema: { time: z.string() }, _meta: { [RESOURCE_URI_META_KEY]: resourceUri }, }, async (): Promise => { const time = new Date().toISOString(); return { - content: [{ type: "text", text: time }], - structuredContent: { time }, + content: [{ type: "text", text: JSON.stringify({ time }) }], }; }, ); diff --git a/examples/basic-server-vanillajs/src/mcp-app.ts b/examples/basic-server-vanillajs/src/mcp-app.ts index 9c9364a0..f93dfeea 100644 --- a/examples/basic-server-vanillajs/src/mcp-app.ts +++ b/examples/basic-server-vanillajs/src/mcp-app.ts @@ -15,8 +15,12 @@ const log = { function extractTime(result: CallToolResult): string { - const { time } = (result.structuredContent as { time?: string }) ?? {}; - return time ?? "[ERROR]"; + const text = result.content! + .filter((c): c is { type: "text"; text: string } => c.type === "text") + .map((c) => c.text) + .join(""); + const { time } = JSON.parse(text) as { time: string }; + return time; } diff --git a/examples/budget-allocator-server/server.ts b/examples/budget-allocator-server/server.ts index 9db094d6..0adffd8d 100644 --- a/examples/budget-allocator-server/server.ts +++ b/examples/budget-allocator-server/server.ts @@ -221,30 +221,6 @@ function generateHistory( return months; } -// --------------------------------------------------------------------------- -// Response Formatting -// --------------------------------------------------------------------------- - -function formatBudgetSummary(data: BudgetDataResponse): string { - const lines: string[] = [ - "Budget Allocator Configuration", - "==============================", - "", - `Default Budget: ${data.config.currencySymbol}${data.config.defaultBudget.toLocaleString()}`, - `Available Presets: ${data.config.presetBudgets.map((b) => `${data.config.currencySymbol}${b.toLocaleString()}`).join(", ")}`, - "", - "Categories:", - ...data.config.categories.map( - (c) => ` - ${c.name}: ${c.defaultPercent}% default`, - ), - "", - `Historical Data: ${data.analytics.history.length} months`, - `Benchmark Stages: ${data.analytics.stages.join(", ")}`, - `Default Stage: ${data.analytics.defaultStage}`, - ]; - return lines.join("\n"); -} - // --------------------------------------------------------------------------- // MCP Server Setup // --------------------------------------------------------------------------- @@ -263,7 +239,6 @@ server.registerTool( description: "Returns budget configuration with 24 months of historical allocations and industry benchmarks by company stage", inputSchema: {}, - outputSchema: BudgetDataResponseSchema.shape, _meta: { [RESOURCE_URI_META_KEY]: resourceUri }, }, async (): Promise => { @@ -292,10 +267,9 @@ server.registerTool( content: [ { type: "text", - text: formatBudgetSummary(response), + text: JSON.stringify(response), }, ], - structuredContent: response, }; }, ); diff --git a/examples/budget-allocator-server/src/mcp-app.ts b/examples/budget-allocator-server/src/mcp-app.ts index c2672216..723dc060 100644 --- a/examples/budget-allocator-server/src/mcp-app.ts +++ b/examples/budget-allocator-server/src/mcp-app.ts @@ -606,7 +606,13 @@ const app = new App({ name: "Budget Allocator", version: "1.0.0" }); app.ontoolresult = (result) => { log.info("Received tool result:", result); - const data = result.structuredContent as unknown as BudgetDataResponse; + const text = result + .content!.filter( + (c): c is { type: "text"; text: string } => c.type === "text", + ) + .map((c) => c.text) + .join(""); + const data = JSON.parse(text) as BudgetDataResponse; if (data?.config && data?.analytics) { initializeUI(data.config, data.analytics); } diff --git a/examples/cohort-heatmap-server/server.ts b/examples/cohort-heatmap-server/server.ts index 0c2a3a54..f2454997 100644 --- a/examples/cohort-heatmap-server/server.ts +++ b/examples/cohort-heatmap-server/server.ts @@ -149,17 +149,6 @@ function generateCohortData( }; } -function formatCohortSummary(data: CohortData): string { - const avgRetention = data.cohorts - .flatMap((c) => c.cells) - .filter((cell) => cell.periodIndex > 0) - .reduce((sum, cell, _, arr) => sum + cell.retention / arr.length, 0); - - return `Cohort Analysis: ${data.cohorts.length} cohorts, ${data.periods.length} periods -Average retention: ${(avgRetention * 100).toFixed(1)}% -Metric: ${data.metric}, Period: ${data.periodType}`; -} - const server = new McpServer({ name: "Cohort Heatmap Server", version: "1.0.0", @@ -176,7 +165,6 @@ const server = new McpServer({ description: "Returns cohort retention heatmap data showing customer retention over time by signup month", inputSchema: GetCohortDataInputSchema.shape, - outputSchema: CohortDataSchema.shape, _meta: { [RESOURCE_URI_META_KEY]: resourceUri }, }, async ({ metric, periodType, cohortCount, maxPeriods }) => { @@ -188,8 +176,7 @@ const server = new McpServer({ ); return { - content: [{ type: "text", text: formatCohortSummary(data) }], - structuredContent: data, + content: [{ type: "text", text: JSON.stringify(data) }], }; }, ); diff --git a/examples/cohort-heatmap-server/src/mcp-app.tsx b/examples/cohort-heatmap-server/src/mcp-app.tsx index e27595cf..42270a99 100644 --- a/examples/cohort-heatmap-server/src/mcp-app.tsx +++ b/examples/cohort-heatmap-server/src/mcp-app.tsx @@ -103,7 +103,13 @@ function CohortHeatmapInner({ app }: { app: App }) { maxPeriods: 12, }, }); - setData(result.structuredContent as unknown as CohortData); + const text = result + .content!.filter( + (c): c is { type: "text"; text: string } => c.type === "text", + ) + .map((c) => c.text) + .join(""); + setData(JSON.parse(text) as CohortData); } catch (e) { console.error("Failed to fetch cohort data:", e); } finally { diff --git a/examples/customer-segmentation-server/server.ts b/examples/customer-segmentation-server/server.ts index 5ed99734..8fea3d3d 100644 --- a/examples/customer-segmentation-server/server.ts +++ b/examples/customer-segmentation-server/server.ts @@ -27,29 +27,6 @@ const GetCustomerDataInputSchema = z.object({ .describe("Filter by segment (default: All)"), }); -const CustomerSchema = z.object({ - id: z.string(), - name: z.string(), - segment: z.string(), - annualRevenue: z.number(), - employeeCount: z.number(), - accountAge: z.number(), - engagementScore: z.number(), - supportTickets: z.number(), - nps: z.number(), -}); - -const SegmentSummarySchema = z.object({ - name: z.string(), - count: z.number(), - color: z.string(), -}); - -const GetCustomerDataOutputSchema = z.object({ - customers: z.array(CustomerSchema), - segments: z.array(SegmentSummarySchema), -}); - // Cache generated data for session consistency let cachedCustomers: Customer[] | null = null; let cachedSegments: SegmentSummary[] | null = null; @@ -92,15 +69,13 @@ const server = new McpServer({ description: "Returns customer data with segment information for visualization. Optionally filter by segment.", inputSchema: GetCustomerDataInputSchema.shape, - outputSchema: GetCustomerDataOutputSchema.shape, _meta: { [RESOURCE_URI_META_KEY]: resourceUri }, }, async ({ segment }): Promise => { const data = getCustomerData(segment); return { - content: [{ type: "text", text: JSON.stringify(data, null, 2) }], - structuredContent: data, + content: [{ type: "text", text: JSON.stringify(data) }], }; }, ); diff --git a/examples/customer-segmentation-server/src/mcp-app.ts b/examples/customer-segmentation-server/src/mcp-app.ts index 3d9b2f7f..757937cb 100644 --- a/examples/customer-segmentation-server/src/mcp-app.ts +++ b/examples/customer-segmentation-server/src/mcp-app.ts @@ -336,7 +336,13 @@ async function fetchData(): Promise { arguments: {}, }); - const data = result.structuredContent as unknown as { + const text = result + .content!.filter( + (c): c is { type: "text"; text: string } => c.type === "text", + ) + .map((c) => c.text) + .join(""); + const data = JSON.parse(text) as { customers: Customer[]; segments: SegmentSummary[]; }; diff --git a/examples/scenario-modeler-server/server.ts b/examples/scenario-modeler-server/server.ts index 170d754e..d1178a67 100644 --- a/examples/scenario-modeler-server/server.ts +++ b/examples/scenario-modeler-server/server.ts @@ -61,13 +61,6 @@ const GetScenarioDataInputSchema = z.object({ ), }); -const GetScenarioDataOutputSchema = z.object({ - templates: z.array(ScenarioTemplateSchema), - defaultInputs: ScenarioInputsSchema, - customProjections: z.array(MonthlyProjectionSchema).optional(), - customSummary: ScenarioSummarySchema.optional(), -}); - // Types derived from schemas type ScenarioInputs = z.infer; type MonthlyProjection = z.infer; @@ -247,37 +240,6 @@ const DEFAULT_INPUTS: ScenarioInputs = { fixedCosts: 30000, }; -// ============================================================================ -// Formatters for text output -// ============================================================================ - -function formatCurrency(value: number): string { - const absValue = Math.abs(value); - const sign = value < 0 ? "-" : ""; - if (absValue >= 1_000_000) { - return `${sign}$${(absValue / 1_000_000).toFixed(2)}M`; - } - if (absValue >= 1_000) { - return `${sign}$${(absValue / 1_000).toFixed(1)}K`; - } - return `${sign}$${Math.round(absValue)}`; -} - -function formatScenarioSummary( - summary: ScenarioSummary, - label: string, -): string { - return [ - `${label}:`, - ` Ending MRR: ${formatCurrency(summary.endingMRR)}`, - ` ARR: ${formatCurrency(summary.arr)}`, - ` Total Revenue: ${formatCurrency(summary.totalRevenue)}`, - ` Total Profit: ${formatCurrency(summary.totalProfit)}`, - ` MRR Growth: ${summary.mrrGrowthPct.toFixed(1)}%`, - ` Break-even: ${summary.breakEvenMonth ? `Month ${summary.breakEvenMonth}` : "Not achieved"}`, - ].join("\n"); -} - // ============================================================================ // MCP Server // ============================================================================ @@ -298,7 +260,6 @@ const server = new McpServer({ description: "Returns SaaS scenario templates and optionally computes custom projections for given inputs", inputSchema: GetScenarioDataInputSchema.shape, - outputSchema: GetScenarioDataOutputSchema.shape, _meta: { [RESOURCE_URI_META_KEY]: resourceUri }, }, async (args: { @@ -308,28 +269,18 @@ const server = new McpServer({ ? calculateScenario(args.customInputs) : undefined; - const text = [ - "SaaS Scenario Modeler", - "=".repeat(40), - "", - "Available Templates:", - ...SCENARIO_TEMPLATES.map( - (t) => ` ${t.icon} ${t.name}: ${t.description}`, - ), - "", - customScenario - ? formatScenarioSummary(customScenario.summary, "Custom Scenario") - : "Use customInputs parameter to compute projections for a specific scenario.", - ].join("\n"); - return { - content: [{ type: "text", text }], - structuredContent: { - templates: SCENARIO_TEMPLATES, - defaultInputs: DEFAULT_INPUTS, - customProjections: customScenario?.projections, - customSummary: customScenario?.summary, - }, + content: [ + { + type: "text", + text: JSON.stringify({ + templates: SCENARIO_TEMPLATES, + defaultInputs: DEFAULT_INPUTS, + customProjections: customScenario?.projections, + customSummary: customScenario?.summary, + }), + }, + ], }; }, ); diff --git a/examples/scenario-modeler-server/src/mcp-app.tsx b/examples/scenario-modeler-server/src/mcp-app.tsx index 4f5b1e50..9fa411f3 100644 --- a/examples/scenario-modeler-server/src/mcp-app.tsx +++ b/examples/scenario-modeler-server/src/mcp-app.tsx @@ -24,11 +24,15 @@ interface CallToolResultData { defaultInputs?: ScenarioInputs; } -/** Extract templates and defaultInputs from tool result structuredContent */ +/** Extract templates and defaultInputs from tool result content */ function extractResultData(result: CallToolResult): CallToolResultData { - if (!result.structuredContent) return {}; - const { templates, defaultInputs } = - result.structuredContent as CallToolResultData; + const text = result + .content!.filter( + (c): c is { type: "text"; text: string } => c.type === "text", + ) + .map((c) => c.text) + .join(""); + const { templates, defaultInputs } = JSON.parse(text) as CallToolResultData; return { templates, defaultInputs }; } diff --git a/examples/system-monitor-server/server.ts b/examples/system-monitor-server/server.ts index bbb2ba36..259c0645 100644 --- a/examples/system-monitor-server/server.ts +++ b/examples/system-monitor-server/server.ts @@ -121,7 +121,6 @@ const server = new McpServer({ description: "Returns current system statistics including per-core CPU usage, memory, and system info.", inputSchema: {}, - outputSchema: SystemStatsSchema.shape, _meta: { [RESOURCE_URI_META_KEY]: resourceUri }, }, async (): Promise => { @@ -148,8 +147,7 @@ const server = new McpServer({ }; return { - content: [{ type: "text", text: JSON.stringify(stats, null, 2) }], - structuredContent: stats, + content: [{ type: "text", text: JSON.stringify(stats) }], }; }, ); diff --git a/examples/system-monitor-server/src/mcp-app.ts b/examples/system-monitor-server/src/mcp-app.ts index 7c645e03..19e53c6a 100644 --- a/examples/system-monitor-server/src/mcp-app.ts +++ b/examples/system-monitor-server/src/mcp-app.ts @@ -264,7 +264,13 @@ async function fetchStats(): Promise { arguments: {}, }); - const stats = result.structuredContent as unknown as SystemStats; + const text = result + .content!.filter( + (c): c is { type: "text"; text: string } => c.type === "text", + ) + .map((c) => c.text) + .join(""); + const stats = JSON.parse(text) as SystemStats; // Initialize chart on first data if needed if (!state.chart && stats.cpu.count > 0) {