@atlassian/mcp-compressor 0.21.3 → 0.22.1

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.
@@ -1,4 +1,4 @@
1
- import { stringifyToolResult } from "./tool_specs.js";
1
+ import { normalizeHostToolResult } from "./rust_core.js";
2
2
  export async function startLocalToolBridge(tools) {
3
3
  const http = await import("node:http");
4
4
  const token = crypto.randomUUID();
@@ -28,7 +28,7 @@ export async function startLocalToolBridge(tools) {
28
28
  const result = await tool.execute(body.input ?? body.tool_input ?? {});
29
29
  response
30
30
  .writeHead(200, { "content-type": "application/json" })
31
- .end(JSON.stringify({ result: stringifyToolResult(result) }));
31
+ .end(JSON.stringify({ result: normalizeHostToolResult(result, false) }));
32
32
  }
33
33
  catch (error) {
34
34
  response
package/dist/native.d.ts CHANGED
@@ -10,6 +10,8 @@ export interface NativeCore {
10
10
  parseToolArgvJson(toolJson: string, argvJson: string): string;
11
11
  generateClientArtifactsJson(kind: string, configJson: string): string;
12
12
  generateClientArtifactFilesJson(kind: string, configJson: string): string;
13
+ buildHostTransformPlanJson(configJson: string): string;
14
+ normalizeHostToolResultJson(valueJson: string, toonify: boolean): string;
13
15
  normalizeServersJson(serversJson: string): string;
14
16
  parseMcpConfigJson(configJson: string): string;
15
17
  rememberOauthBackendJson(backendUri: string, backendName: string, storeDir: string): void;
@@ -100,3 +100,34 @@ export interface OAuthStoreEntry {
100
100
  }
101
101
  export declare function listOAuthCredentials(): OAuthStoreEntry[];
102
102
  export declare function clearOAuthCredentials(target?: string | null): string[];
103
+ export interface HostTransformPlanConfig {
104
+ kind: ClientArtifactKind | "just-bash";
105
+ serverName: string;
106
+ tools: ToolSpec[];
107
+ outputDir?: string;
108
+ commandName?: string;
109
+ bridgeUrl?: string;
110
+ token?: string;
111
+ sessionPid?: number;
112
+ }
113
+ export interface HostTransformPlan {
114
+ helpToolName: string;
115
+ helpDescription: string;
116
+ outputDir?: string;
117
+ files: Record<string, string>;
118
+ paths: string[];
119
+ environment: Record<string, string>;
120
+ justBash?: {
121
+ providerName: string;
122
+ commandName: string;
123
+ helpToolName: string;
124
+ commands: Array<{
125
+ commandName: string;
126
+ backendToolName: string;
127
+ description?: string;
128
+ inputSchema: unknown;
129
+ }>;
130
+ };
131
+ }
132
+ export declare function buildHostTransformPlan(config: HostTransformPlanConfig): HostTransformPlan;
133
+ export declare function normalizeHostToolResult(value: unknown, toonify: boolean): string;
package/dist/rust_core.js CHANGED
@@ -107,3 +107,18 @@ export function listOAuthCredentials() {
107
107
  export function clearOAuthCredentials(target) {
108
108
  return JSON.parse(loadNativeCore().clearOauthCredentialsJson(target ?? null));
109
109
  }
110
+ export function buildHostTransformPlan(config) {
111
+ return JSON.parse(loadNativeCore().buildHostTransformPlanJson(stringify({
112
+ kind: config.kind,
113
+ serverName: config.serverName,
114
+ tools: config.tools.map(toNativeTool),
115
+ outputDir: config.outputDir,
116
+ commandName: config.commandName,
117
+ bridgeUrl: config.bridgeUrl,
118
+ token: config.token,
119
+ sessionPid: config.sessionPid,
120
+ })));
121
+ }
122
+ export function normalizeHostToolResult(value, toonify) {
123
+ return loadNativeCore().normalizeHostToolResultJson(stringify(value), toonify);
124
+ }
@@ -19,10 +19,19 @@ export function normalizeServerName(name) {
19
19
  export function stringifyToolResult(value) {
20
20
  if (typeof value === "string")
21
21
  return value;
22
- const mcpText = stringifyMcpTextContent(value);
22
+ const unwrapped = unwrapSingleResultWrapper(value);
23
+ if (typeof unwrapped === "string")
24
+ return unwrapped;
25
+ const mcpText = stringifyMcpTextContent(unwrapped);
23
26
  if (mcpText !== undefined)
24
27
  return mcpText;
25
- return JSON.stringify(value);
28
+ return JSON.stringify(unwrapped);
29
+ }
30
+ function unwrapSingleResultWrapper(value) {
31
+ if (!value || typeof value !== "object" || Array.isArray(value))
32
+ return value;
33
+ const entries = Object.entries(value);
34
+ return entries.length === 1 && entries[0]?.[0] === "result" ? entries[0][1] : value;
26
35
  }
27
36
  function stringifyMcpTextContent(value) {
28
37
  if (!value || typeof value !== "object" || Array.isArray(value))
@@ -1,5 +1,4 @@
1
1
  import type { ExecutableTool } from "./adapters.js";
2
- import { type GeneratedBridgeClientArtifactsResult } from "./generated_clients.js";
3
2
  import { type JustBashCommandRegistration } from "./just_bash_commands.js";
4
3
  export interface TransformToolOptions {
5
4
  serverName?: string;
@@ -19,7 +18,12 @@ export interface TransformToolsForCodeModeOptions extends TransformToolOptions {
19
18
  export interface TransformToolsForCliModeOptions extends TransformToolOptions {
20
19
  outputDir?: string;
21
20
  }
22
- export interface GeneratedToolTransformResult extends GeneratedBridgeClientArtifactsResult {
21
+ export interface GeneratedToolTransformResult {
22
+ paths: string[];
23
+ files: Record<string, string>;
24
+ environment: Record<string, string>;
25
+ bridgeUrl: string;
26
+ token: string;
23
27
  tools: Record<string, ExecutableTool>;
24
28
  close(): void;
25
29
  }
@@ -1,24 +1,19 @@
1
- import { generateClientFromBridge, } from "./generated_clients.js";
2
1
  import { createJustBashCommandRegistrations, installJustBashRegistrations, } from "./just_bash_commands.js";
3
2
  import { startLocalToolBridge } from "./local_tool_bridge.js";
4
- import { maybeToonifyOutput } from "./rust_core.js";
5
- import { executableToolToSpec, executableToolsToSpecs, normalizeServerName, stringifyToolResult, } from "./tool_specs.js";
3
+ import { buildHostTransformPlan, normalizeHostToolResult, } from "./rust_core.js";
4
+ import { executableToolToSpec, executableToolsToSpecs, normalizeServerName } from "./tool_specs.js";
6
5
  export function transformToolsForJustBash(tools, options) {
7
6
  const serverName = normalizeServerName(options.serverName);
8
- const registrations = createJustBashCommandRegistrations(Object.entries(tools).map(([name, tool]) => justBashSource(serverName, name, tool)));
7
+ const toolSpecs = executableToolsToSpecs(tools);
8
+ const plan = buildHostTransformPlan({ kind: "just-bash", serverName, tools: toolSpecs });
9
+ const registrations = createJustBashCommandRegistrations((plan.justBash?.commands ?? []).map((command) => justBashSource(serverName, command.commandName, command.backendToolName, tools)));
9
10
  installJustBashRegistrations(options.bash, registrations);
10
- const helpDescription = shellToolHelpDescription({
11
- command: serverName,
12
- cliName: serverName,
13
- tools: executableToolsToSpecs(tools),
14
- commandNameForTool: cliSubcommandName,
15
- });
16
11
  return {
17
12
  registrations,
18
13
  tools: helpTools({
19
- serverName,
20
- description: helpDescription,
21
- output: helpDescription,
14
+ name: plan.helpToolName,
15
+ description: plan.helpDescription,
16
+ output: plan.helpDescription,
22
17
  }),
23
18
  };
24
19
  }
@@ -32,158 +27,59 @@ export async function transformToolsForCodeMode(tools, options) {
32
27
  }
33
28
  export async function transformToolsForCliMode(tools, options = {}) {
34
29
  const serverName = normalizeServerName(options.serverName);
35
- const output = options.outputDir === undefined ? defaultCliOutputDir() : { outputDir: options.outputDir };
36
30
  return generatedTransform(tools, {
37
31
  kind: "cli",
38
32
  serverName,
39
- outputDir: output.outputDir,
33
+ outputDir: options.outputDir,
40
34
  commandName: serverName,
41
35
  });
42
36
  }
43
- function justBashSource(serverName, name, tool) {
37
+ function justBashSource(serverName, commandName, backendToolName, tools) {
38
+ const tool = tools[backendToolName];
39
+ if (tool === undefined) {
40
+ throw new Error(`missing Just Bash backend tool: ${backendToolName}`);
41
+ }
44
42
  return {
45
43
  providerName: serverName,
46
- commandName: `${serverName}_${name}`,
47
- backendToolName: name,
44
+ commandName,
45
+ backendToolName,
48
46
  helpToolName: `${serverName}_help`,
49
- tool: executableToolToSpec(name, tool),
50
- invoke: async (input) => maybeToonifyOutput(stringifyToolResult(await tool.execute(input))),
47
+ tool: executableToolToSpec(backendToolName, tool),
48
+ invoke: async (input) => normalizeHostToolResult(await tool.execute(input), true),
51
49
  };
52
50
  }
53
51
  async function generatedTransform(tools, options) {
54
52
  const bridge = await startLocalToolBridge(tools);
55
- const generated = generateClientFromBridge({
56
- kind: options.kind,
57
- name: options.serverName,
58
- bridgeUrl: bridge.bridgeUrl,
59
- token: bridge.token,
60
- tools: executableToolsToSpecs(tools),
61
- outputDir: options.outputDir,
62
- });
63
- const helpDescription = generatedHelpDescription({
53
+ const plan = buildHostTransformPlan({
64
54
  kind: options.kind,
65
55
  serverName: options.serverName,
56
+ tools: executableToolsToSpecs(tools),
66
57
  outputDir: options.outputDir,
67
- files: Object.keys(generated.files),
68
58
  commandName: options.commandName,
69
- tools: executableToolsToSpecs(tools),
59
+ bridgeUrl: bridge.bridgeUrl,
60
+ token: bridge.token,
70
61
  });
71
62
  return {
72
- ...generated,
63
+ paths: plan.paths,
64
+ files: plan.files,
65
+ environment: plan.environment,
66
+ bridgeUrl: bridge.bridgeUrl,
67
+ token: bridge.token,
73
68
  tools: helpTools({
74
- serverName: options.serverName,
75
- description: helpDescription,
76
- output: helpDescription,
69
+ name: plan.helpToolName,
70
+ description: plan.helpDescription,
71
+ output: plan.helpDescription,
77
72
  }),
78
73
  close: () => bridge.close(),
79
74
  };
80
75
  }
81
76
  function helpTools(options) {
82
- const name = `${options.serverName}_help`;
83
77
  return {
84
- [name]: {
85
- name,
78
+ [options.name]: {
79
+ name: options.name,
86
80
  description: options.description,
87
81
  inputSchema: { type: "object", properties: {} },
88
82
  execute: async () => options.output,
89
83
  },
90
84
  };
91
85
  }
92
- function generatedHelpDescription(options) {
93
- if (options.kind === "cli") {
94
- const command = options.commandName ?? `${options.outputDir}/${options.serverName}`;
95
- return cliHelpDescription(command, options.serverName, options.tools);
96
- }
97
- return codeHelpDescription(options);
98
- }
99
- function cliHelpDescription(command, cliName, tools) {
100
- return shellToolHelpDescription({
101
- command,
102
- cliName,
103
- tools,
104
- commandNameForTool: cliSubcommandName,
105
- });
106
- }
107
- function shellToolHelpDescription(options) {
108
- return [
109
- `Functionality associated with the ${options.cliName} toolset is provided via the \`${options.command}\` CLI. Do not call this tool - use the CLI instead.`,
110
- `${options.cliName} - the ${options.cliName} toolset`,
111
- "",
112
- "When relevant, outputs from this CLI will prefer using the TOON format for more efficient representation of data.",
113
- "",
114
- "USAGE:",
115
- ` ${options.command} <subcommand> [options]`,
116
- "",
117
- "SUBCOMMANDS:",
118
- ...formatSubcommands(options.tools, options.commandNameForTool),
119
- "",
120
- `Run '${options.command} --help' in the shell for usage.`,
121
- `Run '${options.command} <subcommand> --help' for per-command help.`,
122
- `Run '${options.command} <subcommand> [options]' to invoke a tool.`,
123
- ].join("\n");
124
- }
125
- function formatSubcommands(tools, commandNameForTool) {
126
- const maxNameLength = Math.max(...tools.map((tool) => commandNameForTool(tool.name).length), 0);
127
- return tools.map((tool) => {
128
- const subcommand = commandNameForTool(tool.name);
129
- const description = compactDescription(tool.description ?? undefined);
130
- return ` ${subcommand.padEnd(maxNameLength + 2)}${description}`.trimEnd();
131
- });
132
- }
133
- function cliSubcommandName(toolName) {
134
- return toolName.replaceAll("_", "-");
135
- }
136
- function compactDescription(description) {
137
- return (description ?? "").replace(/\s+/g, " ").trim();
138
- }
139
- function codeHelpDescription(options) {
140
- const language = options.kind === "python" ? "Python" : "TypeScript";
141
- const languageLower = options.kind === "python" ? "python" : "typescript";
142
- const moduleName = options.kind === "python" ? `${options.serverName}.py` : `${options.serverName}.ts`;
143
- const functions = formatCodeFunctions(options.kind, options.tools);
144
- const details = options.kind === "python"
145
- ? [
146
- "For details on a specific function, run:",
147
- "```python",
148
- `from ${options.serverName} import <function>`,
149
- "print(help(<function>))",
150
- "```",
151
- ]
152
- : [
153
- "For details on a specific function, inspect the generated TypeScript declarations or editor hover documentation.",
154
- `Primary declarations: ${options.outputDir}/${options.serverName}.d.ts`,
155
- ];
156
- return [
157
- `Functionality associated with the ${options.serverName} toolset is provided via a ${language} module. Do not call this tool - import and use the ${languageLower} functionality instead.`,
158
- `${options.serverName} - the ${options.serverName} toolset`,
159
- "",
160
- `${language} source code is available in ${options.outputDir}/${moduleName}`,
161
- "",
162
- "Available functions:",
163
- ...functions,
164
- "",
165
- ...details,
166
- ].join("\n");
167
- }
168
- function formatCodeFunctions(kind, tools) {
169
- const names = tools.map((tool) => (kind === "python" ? tool.name : snakeToCamel(tool.name)));
170
- const maxNameLength = Math.max(...names.map((name) => name.length), 0);
171
- return tools.map((tool, index) => {
172
- const name = names[index] ?? tool.name;
173
- return ` ${name.padEnd(maxNameLength + 2)}${compactDescription(tool.description ?? undefined)}`.trimEnd();
174
- });
175
- }
176
- function snakeToCamel(name) {
177
- return name.replace(/[_-]([a-zA-Z0-9])/g, (_match, char) => char.toUpperCase());
178
- }
179
- function defaultCliOutputDir() {
180
- const envDir = process.env.MCP_COMPRESSOR_CLI_OUTPUT_DIR;
181
- if (envDir !== undefined && envDir.length > 0) {
182
- return { outputDir: envDir };
183
- }
184
- const home = process.env.HOME;
185
- if (home !== undefined && home.length > 0) {
186
- return { outputDir: `${home}/.local/bin` };
187
- }
188
- return { outputDir: "./dist" };
189
- }
package/native/index.d.ts CHANGED
@@ -6,6 +6,8 @@ export declare class NativeCompressedSession {
6
6
  updateAuthProviderHeadersJson(providerIndex: number, headersJson: string): void
7
7
  }
8
8
 
9
+ export declare function buildHostTransformPlanJson(configJson: string): string
10
+
9
11
  export declare function clearOauthCredentialsJson(target?: string | undefined | null): string
10
12
 
11
13
  export declare function compressToolListingJson(level: string, toolsJson: string): string
@@ -20,6 +22,8 @@ export declare function listOauthCredentialsJson(): string
20
22
 
21
23
  export declare function maybeToonifyOutputJson(output: string): string
22
24
 
25
+ export declare function normalizeHostToolResultJson(valueJson: string, toonify: boolean): string
26
+
23
27
  export declare function normalizeServersJson(serversJson: string): string
24
28
 
25
29
  export declare function parseMcpConfigJson(configJson: string): string
Binary file
Binary file
package/native/index.js CHANGED
@@ -577,6 +577,7 @@ if (!nativeBinding) {
577
577
 
578
578
  module.exports = nativeBinding
579
579
  module.exports.NativeCompressedSession = nativeBinding.NativeCompressedSession
580
+ module.exports.buildHostTransformPlanJson = nativeBinding.buildHostTransformPlanJson
580
581
  module.exports.clearOauthCredentialsJson = nativeBinding.clearOauthCredentialsJson
581
582
  module.exports.compressToolListingJson = nativeBinding.compressToolListingJson
582
583
  module.exports.formatToolSchemaResponseJson = nativeBinding.formatToolSchemaResponseJson
@@ -584,6 +585,7 @@ module.exports.generateClientArtifactFilesJson = nativeBinding.generateClientArt
584
585
  module.exports.generateClientArtifactsJson = nativeBinding.generateClientArtifactsJson
585
586
  module.exports.listOauthCredentialsJson = nativeBinding.listOauthCredentialsJson
586
587
  module.exports.maybeToonifyOutputJson = nativeBinding.maybeToonifyOutputJson
588
+ module.exports.normalizeHostToolResultJson = nativeBinding.normalizeHostToolResultJson
587
589
  module.exports.normalizeServersJson = nativeBinding.normalizeServersJson
588
590
  module.exports.parseMcpConfigJson = nativeBinding.parseMcpConfigJson
589
591
  module.exports.parseToolArgvJson = nativeBinding.parseToolArgvJson
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlassian/mcp-compressor",
3
- "version": "0.21.3",
3
+ "version": "0.22.1",
4
4
  "description": "TypeScript MCP server wrapper for reducing tokens consumed by MCP tools.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/atlassian-labs/mcp-compressor",