Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/jscpd-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,8 @@
"typescript": "^5.9.3",
"vitest": "^4.1.9"
},
"optionalDependencies": {
"@blackwell-systems/gcf": "2.2.1"
},
"preferGlobal": true
}
6 changes: 6 additions & 0 deletions apps/jscpd-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ 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",
// 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)",
);

addCommonOptions(cli);
Expand Down
8 changes: 5 additions & 3 deletions apps/jscpd-server/src/server/mcp-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Comment thread
coderabbitai[bot] marked this conversation as resolved.

export const createMcpServer = (service: JscpdServerService) => {
const server = new McpServer(
Expand Down Expand Up @@ -46,7 +47,7 @@ export const createMcpServer = (service: JscpdServerService) => {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
text: serialize(result),
},
],
};
Expand Down Expand Up @@ -79,7 +80,7 @@ export const createMcpServer = (service: JscpdServerService) => {
content: [
{
type: "text",
text: JSON.stringify(stats, null, 2),
text: serialize(stats),
},
],
};
Expand Down Expand Up @@ -113,7 +114,7 @@ export const createMcpServer = (service: JscpdServerService) => {
content: [
{
type: "text",
text: JSON.stringify(statistics),
text: serialize(statistics),
},
],
};
Expand Down Expand Up @@ -145,6 +146,7 @@ export const createMcpServer = (service: JscpdServerService) => {
contents: [
{
uri: uri.href,
// Resource declares mimeType: "application/json", so always use JSON here.
text: JSON.stringify(stats, null, 2),
},
],
Expand Down
51 changes: 51 additions & 0 deletions apps/jscpd-server/src/server/serialize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* 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;

/**
* 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 =
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()) {
try {
return encodeGeneric(data);
} catch {
// Fall back to JSON on any encoding error
}
}
return JSON.stringify(data, null, indent);
}