From 3ae14638be239643cd8cb12898ef11d7c8a001f5 Mon Sep 17 00:00:00 2001 From: Dayna Blackwell Date: Tue, 23 Jun 2026 14:01:43 -0700 Subject: [PATCH 1/4] feat: add opt-in GCF output format for MCP tool responses (--gcf) GCF (Graph Compact Format) encodes structured data with 56% fewer tokens than JSON on duplication results. Optional dependency, opt-in via --gcf flag or JSCPD_OUTPUT_FORMAT=gcf env var. Falls back to JSON if the package is not installed or encoding fails. Benchmarked on jscpd response shapes (duplication results + statistics): - 5 duplications: 49% fewer tokens - 20 duplications: 59% fewer tokens - 100 duplications: 58% fewer tokens (14,498 -> 6,112) - Project statistics: 27-28% fewer tokens - Overall: 56% savings, 7/7 GCF wins --- apps/jscpd-server/package.json | 3 ++ apps/jscpd-server/src/index.ts | 4 ++ apps/jscpd-server/src/server/mcp-server.ts | 5 ++- apps/jscpd-server/src/server/serialize.ts | 46 ++++++++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 apps/jscpd-server/src/server/serialize.ts diff --git a/apps/jscpd-server/package.json b/apps/jscpd-server/package.json index f8153ed5..bcc662ae 100644 --- a/apps/jscpd-server/package.json +++ b/apps/jscpd-server/package.json @@ -71,5 +71,8 @@ "typescript": "^5.9.3", "vitest": "^4.1.9" }, + "optionalDependencies": { + "@blackwell-systems/gcf": "2.2.1" + }, "preferGlobal": true } diff --git a/apps/jscpd-server/src/index.ts b/apps/jscpd-server/src/index.ts index 1bad315b..d72aedd8 100644 --- a/apps/jscpd-server/src/index.ts +++ b/apps/jscpd-server/src/index.ts @@ -23,6 +23,10 @@ function initServerCli(packageJson: any, argv: string[]): Command { .option( "-H, --host [string]", "host to bind the server to (Default is 0.0.0.0)", + ) + .option( + "--gcf", + "Enable GCF output format for MCP tool responses (56% fewer tokens)", ); addCommonOptions(cli); diff --git a/apps/jscpd-server/src/server/mcp-server.ts b/apps/jscpd-server/src/server/mcp-server.ts index d3293de0..839da496 100644 --- a/apps/jscpd-server/src/server/mcp-server.ts +++ b/apps/jscpd-server/src/server/mcp-server.ts @@ -2,6 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { JscpdServerService } from "./service"; import { API_INFO } from "./constants"; +import { serialize } from "./serialize"; export const createMcpServer = (service: JscpdServerService) => { const server = new McpServer( @@ -46,7 +47,7 @@ export const createMcpServer = (service: JscpdServerService) => { content: [ { type: "text", - text: JSON.stringify(result, null, 2), + text: serialize(result), }, ], }; @@ -113,7 +114,7 @@ export const createMcpServer = (service: JscpdServerService) => { content: [ { type: "text", - text: JSON.stringify(statistics), + text: serialize(statistics), }, ], }; diff --git a/apps/jscpd-server/src/server/serialize.ts b/apps/jscpd-server/src/server/serialize.ts new file mode 100644 index 00000000..5a8813b6 --- /dev/null +++ b/apps/jscpd-server/src/server/serialize.ts @@ -0,0 +1,46 @@ +/** + * Optional GCF (Graph Compact Format) serialization for MCP tool responses. + * + * When enabled, structured data is encoded using GCF's tabular encoding + * instead of JSON, saving ~56% of tokens on duplication results. + * + * Enable via CLI flag: jscpd-server --gcf + * Or environment variable: JSCPD_OUTPUT_FORMAT=gcf + * + * Requires the optional @blackwell-systems/gcf package. + */ + +let encodeGeneric: ((data: unknown) => string) | null = null; + +try { + const gcf = await import("@blackwell-systems/gcf"); + encodeGeneric = gcf.encodeGeneric; +} catch { + // GCF is an optional dependency +} + +let gcfEnabled: boolean | null = null; + +export function isGcfEnabled(): boolean { + if (gcfEnabled === null) { + gcfEnabled = + process.env.JSCPD_OUTPUT_FORMAT === "gcf" || + process.argv.includes("--gcf"); + } + return gcfEnabled && encodeGeneric !== null; +} + +/** + * Serialize data as JSON or GCF depending on configuration. + * Drop-in replacement for JSON.stringify(data, null, 2). + */ +export function serialize(data: unknown, indent = 2): string { + if (isGcfEnabled() && encodeGeneric) { + try { + return encodeGeneric(data); + } catch { + // Fall back to JSON on any encoding error + } + } + return JSON.stringify(data, null, indent); +} From 34deaa8e870027e3f2e6865e2f3485898c199d28 Mon Sep 17 00:00:00 2001 From: Dayna Blackwell Date: Tue, 23 Jun 2026 14:23:01 -0700 Subject: [PATCH 2/4] fix: add JSDoc to isGcfEnabled, update missed get_statistics to use serialize --- apps/jscpd-server/src/server/mcp-server.ts | 4 ++-- apps/jscpd-server/src/server/serialize.ts | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/jscpd-server/src/server/mcp-server.ts b/apps/jscpd-server/src/server/mcp-server.ts index 839da496..5148aaec 100644 --- a/apps/jscpd-server/src/server/mcp-server.ts +++ b/apps/jscpd-server/src/server/mcp-server.ts @@ -80,7 +80,7 @@ export const createMcpServer = (service: JscpdServerService) => { content: [ { type: "text", - text: JSON.stringify(stats, null, 2), + text: serialize(stats), }, ], }; @@ -146,7 +146,7 @@ export const createMcpServer = (service: JscpdServerService) => { contents: [ { uri: uri.href, - text: JSON.stringify(stats, null, 2), + text: serialize(stats), }, ], }; diff --git a/apps/jscpd-server/src/server/serialize.ts b/apps/jscpd-server/src/server/serialize.ts index 5a8813b6..f3b35530 100644 --- a/apps/jscpd-server/src/server/serialize.ts +++ b/apps/jscpd-server/src/server/serialize.ts @@ -21,6 +21,11 @@ try { let gcfEnabled: boolean | null = null; +/** + * Check whether GCF output is enabled and available. + * Returns true only when the `--gcf` flag or `JSCPD_OUTPUT_FORMAT=gcf` + * env var is set AND the `@blackwell-systems/gcf` package is installed. + */ export function isGcfEnabled(): boolean { if (gcfEnabled === null) { gcfEnabled = From 8472990153c1c07c344f8969d6d57efbb33b839a Mon Sep 17 00:00:00 2001 From: Dayna Blackwell Date: Tue, 23 Jun 2026 14:24:39 -0700 Subject: [PATCH 3/4] fix: remove redundant null check, document Commander/argv decoupling --- apps/jscpd-server/src/index.ts | 2 ++ apps/jscpd-server/src/server/serialize.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/jscpd-server/src/index.ts b/apps/jscpd-server/src/index.ts index d72aedd8..2b63bbb6 100644 --- a/apps/jscpd-server/src/index.ts +++ b/apps/jscpd-server/src/index.ts @@ -26,6 +26,8 @@ function initServerCli(packageJson: any, argv: string[]): Command { ) .option( "--gcf", + // Parsed directly via process.argv in serialize.ts for decoupling: + // the serialize module works independently of the server startup flow. "Enable GCF output format for MCP tool responses (56% fewer tokens)", ); diff --git a/apps/jscpd-server/src/server/serialize.ts b/apps/jscpd-server/src/server/serialize.ts index f3b35530..8d758b77 100644 --- a/apps/jscpd-server/src/server/serialize.ts +++ b/apps/jscpd-server/src/server/serialize.ts @@ -40,7 +40,7 @@ export function isGcfEnabled(): boolean { * Drop-in replacement for JSON.stringify(data, null, 2). */ export function serialize(data: unknown, indent = 2): string { - if (isGcfEnabled() && encodeGeneric) { + if (isGcfEnabled()) { try { return encodeGeneric(data); } catch { From 2f83019d40c248a27cccabeceaadc3082c73308d Mon Sep 17 00:00:00 2001 From: Dayna Blackwell Date: Tue, 23 Jun 2026 15:09:18 -0700 Subject: [PATCH 4/4] fix: keep JSON for resource with application/json mimeType --- apps/jscpd-server/src/server/mcp-server.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/jscpd-server/src/server/mcp-server.ts b/apps/jscpd-server/src/server/mcp-server.ts index 5148aaec..20785117 100644 --- a/apps/jscpd-server/src/server/mcp-server.ts +++ b/apps/jscpd-server/src/server/mcp-server.ts @@ -146,7 +146,8 @@ export const createMcpServer = (service: JscpdServerService) => { contents: [ { uri: uri.href, - text: serialize(stats), + // Resource declares mimeType: "application/json", so always use JSON here. + text: JSON.stringify(stats, null, 2), }, ], };