@atlassian/mcp-compressor 0.32.1 → 0.33.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.
- package/README.md +1 -1
- package/dist/cli.js +4 -5
- package/dist/native.d.ts +1 -1
- package/dist/native_client.d.ts +19 -3
- package/dist/native_client.js +115 -29
- package/dist/rust_core.js +8 -2
- package/dist/transforms.d.ts +9 -3
- package/dist/transforms.js +3 -3
- package/native/index.d.ts +1 -1
- package/native/index.darwin-arm64.node +0 -0
- package/native/index.darwin-x64.node +0 -0
- package/native/index.linux-arm64-gnu.node +0 -0
- package/native/index.linux-x64-gnu.node +0 -0
- package/native/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ atlassian search-confluence --query oauth
|
|
|
72
72
|
atlassian get-jira-issue --issue-url https://jira.example.com/browse/PROJ-123
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
Tool output is returned verbatim in CLI mode. Pass `--toonify` to opt in to TOON conversion of JSON, CSV and YAML results.
|
|
76
76
|
|
|
77
77
|
### Bash mode
|
|
78
78
|
|
package/dist/cli.js
CHANGED
|
@@ -61,15 +61,14 @@ function buildProgram(options = {}) {
|
|
|
61
61
|
"invoke_tool, list_tools). The name will be sanitized to conform to MCP\n" +
|
|
62
62
|
"tool name specifications (only A-Z, a-z, 0-9, _, -, .).")
|
|
63
63
|
.option("-l, --log-level <level>", "The logging level.", "error")
|
|
64
|
-
.option("--toonify", "Convert JSON tool responses to TOON format automatically.")
|
|
64
|
+
.option("--toonify", "Convert JSON, CSV and YAML tool responses to TOON format automatically.")
|
|
65
65
|
.option("--cli-mode", "Start in CLI mode: expose a single help MCP tool, start a local HTTP\n" +
|
|
66
66
|
"bridge, and generate a shell script for interacting with the wrapped\n" +
|
|
67
|
-
"server via CLI.
|
|
67
|
+
"server via CLI.")
|
|
68
68
|
.option("--cli-port <port>", "Port for the local CLI bridge HTTP server (default: random free port).")
|
|
69
69
|
.option("--just-bash", "Start in just-bash mode: expose a single 'bash' MCP tool powered by\n" +
|
|
70
70
|
"just-bash, with all backend server tools available as custom commands.\n" +
|
|
71
|
-
"Requires the 'just-bash' package to be installed.
|
|
72
|
-
"automatically enabled in this mode.")
|
|
71
|
+
"Requires the 'just-bash' package to be installed.")
|
|
73
72
|
.option("--include-tool <tool>", "Wrapped server tool name to expose. Can be used multiple times.\n" +
|
|
74
73
|
"If omitted, all tools are included.", collect, [])
|
|
75
74
|
.option("--exclude-tool <tool>", "Wrapped server tool name to hide. Can be used multiple times.", collect, []);
|
|
@@ -85,7 +84,7 @@ function parseCliArgsWithOptions(argv, parseOptions = {}) {
|
|
|
85
84
|
const backend = parseBackendArg(program.args);
|
|
86
85
|
const justBash = parsedOptions.justBash ?? false;
|
|
87
86
|
const cliMode = parsedOptions.cliMode ?? false;
|
|
88
|
-
const toonify =
|
|
87
|
+
const toonify = parsedOptions.toonify ?? false;
|
|
89
88
|
return {
|
|
90
89
|
backend,
|
|
91
90
|
justBash,
|
package/dist/native.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export interface NativeCore {
|
|
|
20
20
|
listOauthCredentialsJson(): string;
|
|
21
21
|
clearOauthCredentialsJson(target?: string | null): string;
|
|
22
22
|
startCompressedSessionJson(configJson: string, backendsJson: string): Promise<NativeCompressedSession>;
|
|
23
|
-
startCompressedSessionWithProviderBackendsJson(configJson: string, backendsJson: string, providersJson: string): Promise<NativeCompressedSession>;
|
|
23
|
+
startCompressedSessionWithProviderBackendsJson(configJson: string, backendsJson: string, providersJson: string, refreshProviders: () => Promise<string>): Promise<NativeCompressedSession>;
|
|
24
24
|
startCompressedSessionFromMcpConfigJson(configJson: string, mcpConfigJson: string): Promise<NativeCompressedSession>;
|
|
25
25
|
}
|
|
26
26
|
export interface NativeCompressedSession {
|
package/dist/native_client.d.ts
CHANGED
|
@@ -54,13 +54,24 @@ export interface GeneratedCodeClient {
|
|
|
54
54
|
environment: Record<string, string>;
|
|
55
55
|
}
|
|
56
56
|
export declare function normalizeServers(servers: NativeServersInput): Promise<NormalizedBackendConfig[] | string>;
|
|
57
|
+
/**
|
|
58
|
+
* Hand a freshly started native session to `build`, closing the session if
|
|
59
|
+
* `build` throws.
|
|
60
|
+
*
|
|
61
|
+
* A started session owns backend processes and sockets that are only reachable
|
|
62
|
+
* through the object `build` returns. Without this the session would leak for
|
|
63
|
+
* the lifetime of the host process whenever adoption failed.
|
|
64
|
+
*
|
|
65
|
+
* Internal; not part of the public package surface.
|
|
66
|
+
*/
|
|
67
|
+
export declare function adoptNativeSession<T>(session: {
|
|
68
|
+
close(): void;
|
|
69
|
+
}, build: () => T): T;
|
|
57
70
|
export declare class CompressorProxy {
|
|
58
71
|
private readonly session;
|
|
59
72
|
private readonly defaultServer;
|
|
60
|
-
private readonly authProviders;
|
|
61
73
|
private closed;
|
|
62
|
-
constructor(session: CompressedSession, defaultServer: string | null
|
|
63
|
-
private refreshAuthProviders;
|
|
74
|
+
constructor(session: CompressedSession, defaultServer: string | null);
|
|
64
75
|
info(): CompressedSessionInfo;
|
|
65
76
|
get bridgeUrl(): string;
|
|
66
77
|
get token(): string;
|
|
@@ -87,10 +98,15 @@ export declare class CompressorProxy {
|
|
|
87
98
|
export declare class CompressorClient {
|
|
88
99
|
private readonly options;
|
|
89
100
|
private session;
|
|
101
|
+
private proxy;
|
|
102
|
+
private connectPromise;
|
|
103
|
+
private closingPromise;
|
|
90
104
|
private readonly mode;
|
|
91
105
|
private authProviders;
|
|
92
106
|
constructor(options: CompressorClientOptions);
|
|
93
107
|
connect(): Promise<CompressorProxy>;
|
|
108
|
+
private startConnection;
|
|
94
109
|
close(): Promise<void>;
|
|
110
|
+
private closeCurrentConnection;
|
|
95
111
|
private defaultServer;
|
|
96
112
|
}
|
package/dist/native_client.js
CHANGED
|
@@ -46,7 +46,7 @@ async function normalizeServersWithProviders(servers) {
|
|
|
46
46
|
for (const [name, config] of Object.entries(servers)) {
|
|
47
47
|
if (typeof config === "object" && config !== null && "url" in config) {
|
|
48
48
|
const provider = providerFromConfig(config);
|
|
49
|
-
const backend = await sdkObjectToNative(name, config, { includeProvider:
|
|
49
|
+
const backend = await sdkObjectToNative(name, config, { includeProvider: provider === undefined });
|
|
50
50
|
if (provider !== undefined) {
|
|
51
51
|
backend.providerIndex = providers.length;
|
|
52
52
|
providers.push(provider);
|
|
@@ -152,25 +152,38 @@ function transformMode(mode) {
|
|
|
152
152
|
return "cli";
|
|
153
153
|
return mode;
|
|
154
154
|
}
|
|
155
|
+
const proxyCloseCallbacks = new WeakMap();
|
|
156
|
+
/**
|
|
157
|
+
* Hand a freshly started native session to `build`, closing the session if
|
|
158
|
+
* `build` throws.
|
|
159
|
+
*
|
|
160
|
+
* A started session owns backend processes and sockets that are only reachable
|
|
161
|
+
* through the object `build` returns. Without this the session would leak for
|
|
162
|
+
* the lifetime of the host process whenever adoption failed.
|
|
163
|
+
*
|
|
164
|
+
* Internal; not part of the public package surface.
|
|
165
|
+
*/
|
|
166
|
+
export function adoptNativeSession(session, build) {
|
|
167
|
+
try {
|
|
168
|
+
return build();
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
try {
|
|
172
|
+
session.close();
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// Closing a session that never became reachable is best effort.
|
|
176
|
+
}
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
155
180
|
export class CompressorProxy {
|
|
156
181
|
session;
|
|
157
182
|
defaultServer;
|
|
158
|
-
authProviders;
|
|
159
183
|
closed = false;
|
|
160
|
-
constructor(session, defaultServer
|
|
184
|
+
constructor(session, defaultServer) {
|
|
161
185
|
this.session = session;
|
|
162
186
|
this.defaultServer = defaultServer;
|
|
163
|
-
this.authProviders = authProviders;
|
|
164
|
-
}
|
|
165
|
-
async refreshAuthProviders() {
|
|
166
|
-
await Promise.all(this.authProviders.map(async (provider, index) => {
|
|
167
|
-
const headers = await provider();
|
|
168
|
-
const materialized = {};
|
|
169
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
170
|
-
materialized[key] = String(value);
|
|
171
|
-
}
|
|
172
|
-
this.session.updateAuthProviderHeaders(index, materialized);
|
|
173
|
-
}));
|
|
174
187
|
}
|
|
175
188
|
info() {
|
|
176
189
|
return this.session.info();
|
|
@@ -208,7 +221,6 @@ export class CompressorProxy {
|
|
|
208
221
|
if (this.closed) {
|
|
209
222
|
throw new Error("Compressor proxy is closed");
|
|
210
223
|
}
|
|
211
|
-
await this.refreshAuthProviders();
|
|
212
224
|
const response = await fetch(`${this.bridgeUrl}/exec`, {
|
|
213
225
|
method: "POST",
|
|
214
226
|
headers: {
|
|
@@ -242,8 +254,18 @@ export class CompressorProxy {
|
|
|
242
254
|
return (await this.invokeWrapper(wrapper, { tool_name: tool, tool_input: toolInput })).text;
|
|
243
255
|
}
|
|
244
256
|
close() {
|
|
257
|
+
if (this.closed) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
245
260
|
this.closed = true;
|
|
246
|
-
|
|
261
|
+
try {
|
|
262
|
+
this.session.close();
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
const onClose = proxyCloseCallbacks.get(this);
|
|
266
|
+
proxyCloseCallbacks.delete(this);
|
|
267
|
+
onClose?.();
|
|
268
|
+
}
|
|
247
269
|
}
|
|
248
270
|
toExecutableTools() {
|
|
249
271
|
const result = {};
|
|
@@ -285,6 +307,9 @@ export class CompressorProxy {
|
|
|
285
307
|
export class CompressorClient {
|
|
286
308
|
options;
|
|
287
309
|
session = null;
|
|
310
|
+
proxy = null;
|
|
311
|
+
connectPromise = null;
|
|
312
|
+
closingPromise = null;
|
|
288
313
|
mode;
|
|
289
314
|
authProviders = [];
|
|
290
315
|
constructor(options) {
|
|
@@ -292,9 +317,32 @@ export class CompressorClient {
|
|
|
292
317
|
this.mode = options.mode ?? "compressed";
|
|
293
318
|
}
|
|
294
319
|
async connect() {
|
|
295
|
-
if (this.
|
|
296
|
-
|
|
320
|
+
if (this.closingPromise) {
|
|
321
|
+
try {
|
|
322
|
+
await this.closingPromise;
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
// The close caller receives the error; cleared state can reconnect independently.
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (this.proxy) {
|
|
329
|
+
return this.proxy;
|
|
330
|
+
}
|
|
331
|
+
if (this.connectPromise) {
|
|
332
|
+
return this.connectPromise;
|
|
333
|
+
}
|
|
334
|
+
const connection = this.startConnection();
|
|
335
|
+
this.connectPromise = connection;
|
|
336
|
+
try {
|
|
337
|
+
return await connection;
|
|
297
338
|
}
|
|
339
|
+
finally {
|
|
340
|
+
if (this.connectPromise === connection) {
|
|
341
|
+
this.connectPromise = null;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
async startConnection() {
|
|
298
346
|
const normalized = await normalizeServersWithProviders(this.options.servers);
|
|
299
347
|
const config = {
|
|
300
348
|
compressionLevel: this.options.compressionLevel ?? "medium",
|
|
@@ -305,17 +353,55 @@ export class CompressorClient {
|
|
|
305
353
|
transformMode: transformMode(this.mode),
|
|
306
354
|
};
|
|
307
355
|
this.authProviders = typeof normalized === "string" ? [] : normalized.providers;
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
356
|
+
const session = typeof normalized === "string"
|
|
357
|
+
? await startCompressedSessionFromMcpConfig(config, normalized)
|
|
358
|
+
: normalized.providers.length > 0
|
|
359
|
+
? await startCompressedSessionWithAuthProviders(config, normalized.backends, normalized.providers)
|
|
360
|
+
: await startCompressedSession(config, normalized.backends);
|
|
361
|
+
const proxy = adoptNativeSession(session, () => {
|
|
362
|
+
const adopted = new CompressorProxy(session, this.defaultServer());
|
|
363
|
+
proxyCloseCallbacks.set(adopted, () => {
|
|
364
|
+
if (this.proxy === adopted) {
|
|
365
|
+
this.proxy = null;
|
|
366
|
+
this.session = null;
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
return adopted;
|
|
370
|
+
});
|
|
371
|
+
this.session = session;
|
|
372
|
+
this.proxy = proxy;
|
|
373
|
+
return proxy;
|
|
374
|
+
}
|
|
375
|
+
close() {
|
|
376
|
+
if (!this.closingPromise) {
|
|
377
|
+
const closing = this.closeCurrentConnection();
|
|
378
|
+
let trackedClosing;
|
|
379
|
+
trackedClosing = closing.finally(() => {
|
|
380
|
+
if (this.closingPromise === trackedClosing) {
|
|
381
|
+
this.closingPromise = null;
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
this.closingPromise = trackedClosing;
|
|
385
|
+
}
|
|
386
|
+
return this.closingPromise;
|
|
387
|
+
}
|
|
388
|
+
async closeCurrentConnection() {
|
|
389
|
+
try {
|
|
390
|
+
await this.connectPromise;
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
// A failed connection has no native session to release.
|
|
394
|
+
}
|
|
395
|
+
const proxy = this.proxy;
|
|
396
|
+
try {
|
|
397
|
+
proxy?.close();
|
|
398
|
+
}
|
|
399
|
+
finally {
|
|
400
|
+
if (this.proxy === proxy) {
|
|
401
|
+
this.proxy = null;
|
|
402
|
+
this.session = null;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
319
405
|
}
|
|
320
406
|
defaultServer() {
|
|
321
407
|
const servers = this.options.servers;
|
package/dist/rust_core.js
CHANGED
|
@@ -88,13 +88,19 @@ export async function startCompressedSession(config, backends) {
|
|
|
88
88
|
const session = await loadNativeCore().startCompressedSessionJson(stringify(toNativeSessionConfig(config)), stringify(backends.map(toNativeBackendConfig)));
|
|
89
89
|
return new CompressedSession(session);
|
|
90
90
|
}
|
|
91
|
+
async function materializeAuthProviderHeaders(providers) {
|
|
92
|
+
return Promise.all(providers.map(async (provider) => {
|
|
93
|
+
const headers = await provider();
|
|
94
|
+
return Object.fromEntries(Object.entries(headers).map(([key, value]) => [key, String(value)]));
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
91
97
|
export async function startCompressedSessionWithAuthProviders(config, backends, providers) {
|
|
92
98
|
const nativeBackends = backends.map((backend) => ({
|
|
93
99
|
...toNativeBackendConfig(backend),
|
|
94
100
|
provider_index: backend.providerIndex ?? null,
|
|
95
101
|
}));
|
|
96
|
-
const initialHeaders = await
|
|
97
|
-
const session = await loadNativeCore().startCompressedSessionWithProviderBackendsJson(stringify(toNativeSessionConfig(config)), stringify(nativeBackends), stringify(initialHeaders));
|
|
102
|
+
const initialHeaders = await materializeAuthProviderHeaders(providers);
|
|
103
|
+
const session = await loadNativeCore().startCompressedSessionWithProviderBackendsJson(stringify(toNativeSessionConfig(config)), stringify(nativeBackends), stringify(initialHeaders), async () => stringify(await materializeAuthProviderHeaders(providers)));
|
|
98
104
|
return new CompressedSession(session);
|
|
99
105
|
}
|
|
100
106
|
export async function startCompressedSessionFromMcpConfig(config, mcpConfigJson) {
|
package/dist/transforms.d.ts
CHANGED
|
@@ -3,10 +3,16 @@ import { type JustBashCommandRegistration } from "./just_bash_commands.js";
|
|
|
3
3
|
export interface TransformToolOptions {
|
|
4
4
|
serverName?: string;
|
|
5
5
|
}
|
|
6
|
-
export interface TransformToolsForJustBashOptions extends TransformToolOptions {
|
|
7
|
-
bash: unknown;
|
|
8
|
-
}
|
|
9
6
|
export interface PlanToolsForJustBashOptions extends TransformToolOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Convert JSON/CSV/YAML tool results to TOON before writing them to stdout.
|
|
9
|
+
* Defaults to ``false`` so Just Bash output matches the generated CLI byte for
|
|
10
|
+
* byte (and stays pipeable into tools such as ``jq``).
|
|
11
|
+
*/
|
|
12
|
+
toonify?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface TransformToolsForJustBashOptions extends PlanToolsForJustBashOptions {
|
|
15
|
+
bash: unknown;
|
|
10
16
|
}
|
|
11
17
|
export interface JustBashTransformResult {
|
|
12
18
|
tools: Record<string, ExecutableTool>;
|
package/dist/transforms.js
CHANGED
|
@@ -6,7 +6,7 @@ export function planToolsForJustBash(tools, options = {}) {
|
|
|
6
6
|
const serverName = normalizeServerName(options.serverName);
|
|
7
7
|
const toolSpecs = executableToolsToSpecs(tools);
|
|
8
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
|
+
const registrations = createJustBashCommandRegistrations((plan.justBash?.commands ?? []).map((command) => justBashSource(serverName, command.commandName, command.backendToolName, tools, options.toonify ?? false)));
|
|
10
10
|
return {
|
|
11
11
|
registrations,
|
|
12
12
|
tools: helpTools({
|
|
@@ -38,7 +38,7 @@ export async function transformToolsForCliMode(tools, options = {}) {
|
|
|
38
38
|
commandName: serverName,
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
-
function justBashSource(serverName, commandName, backendToolName, tools) {
|
|
41
|
+
function justBashSource(serverName, commandName, backendToolName, tools, toonify) {
|
|
42
42
|
const tool = tools[backendToolName];
|
|
43
43
|
if (tool === undefined) {
|
|
44
44
|
throw new Error(`missing Just Bash backend tool: ${backendToolName}`);
|
|
@@ -49,7 +49,7 @@ function justBashSource(serverName, commandName, backendToolName, tools) {
|
|
|
49
49
|
backendToolName,
|
|
50
50
|
helpToolName: `${serverName}_help`,
|
|
51
51
|
tool: executableToolToSpec(backendToolName, tool),
|
|
52
|
-
invoke: async (input) => normalizeHostToolResult(await tool.execute(input),
|
|
52
|
+
invoke: async (input) => normalizeHostToolResult(await tool.execute(input), toonify),
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
async function generatedTransform(tools, options) {
|
package/native/index.d.ts
CHANGED
|
@@ -40,4 +40,4 @@ export declare function startCompressedSessionFromMcpConfigJson(configJson: stri
|
|
|
40
40
|
|
|
41
41
|
export declare function startCompressedSessionJson(configJson: string, backendsJson: string): Promise<NativeCompressedSession>
|
|
42
42
|
|
|
43
|
-
export declare function startCompressedSessionWithProviderBackendsJson(configJson: string, backendsJson: string, providersJson: string): Promise<NativeCompressedSession>
|
|
43
|
+
export declare function startCompressedSessionWithProviderBackendsJson(configJson: string, backendsJson: string, providersJson: string, refreshProviders: RefreshProviders): Promise<NativeCompressedSession>
|
|
Binary file
|
|
Binary file
|
|
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.
|
|
3
|
+
"version": "0.33.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",
|