@respan/cli 0.15.0 → 0.16.0

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.
@@ -0,0 +1,19 @@
1
+ import { BaseCommand } from '../../lib/base-command.js';
2
+ export default class ToolsCall extends BaseCommand {
3
+ static description: string;
4
+ static args: {
5
+ name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static flags: {
8
+ input: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'input-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'mcp-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ };
18
+ run(): Promise<void>;
19
+ }
@@ -0,0 +1,31 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { Args, Flags } from '@oclif/core';
3
+ import { BaseCommand } from '../../lib/base-command.js';
4
+ import { withMcpClient } from '../../lib/mcp-client.js';
5
+ class ToolsCall extends BaseCommand {
6
+ async run() {
7
+ const { args, flags } = await this.parse(ToolsCall);
8
+ this.globalFlags = flags;
9
+ try {
10
+ const input = JSON.parse(flags['input-file'] ? readFileSync(flags['input-file'], 'utf8') : flags.input ?? '{}');
11
+ if (!input || typeof input !== 'object' || Array.isArray(input))
12
+ throw new Error('Tool input must be a JSON object.');
13
+ const result = await withMcpClient(this.getAuth(), flags['mcp-url'], (client) => client.callTool({ name: args.name, arguments: input }));
14
+ this.log(JSON.stringify(result, null, 2));
15
+ if (result.isError)
16
+ this.exit(1);
17
+ }
18
+ catch (error) {
19
+ this.handleError(error);
20
+ }
21
+ }
22
+ }
23
+ ToolsCall.description = 'Call a Respan MCP tool with its exact tool name and JSON arguments. Writes execute immediately; inspect tools list --json before calling.';
24
+ ToolsCall.args = { name: Args.string({ description: 'MCP tool name, e.g. log_list or trace_get', required: true }) };
25
+ ToolsCall.flags = {
26
+ ...BaseCommand.baseFlags,
27
+ input: Flags.string({ description: 'JSON object of MCP tool arguments', exclusive: ['input-file'] }),
28
+ 'input-file': Flags.string({ description: 'File containing a JSON object of MCP tool arguments', exclusive: ['input'] }),
29
+ 'mcp-url': Flags.string({ description: 'Custom MCP server URL (defaults to the active Platform or Enterprise server)' }),
30
+ };
31
+ export default ToolsCall;
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../lib/base-command.js';
2
+ export default class ToolsList extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ 'mcp-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ 'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'base-url': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,22 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../lib/base-command.js';
3
+ import { listMcpTools, withMcpClient } from '../../lib/mcp-client.js';
4
+ class ToolsList extends BaseCommand {
5
+ async run() {
6
+ const { flags } = await this.parse(ToolsList);
7
+ this.globalFlags = flags;
8
+ try {
9
+ const tools = await withMcpClient(this.getAuth(), flags['mcp-url'], listMcpTools);
10
+ this.outputResult(tools, ['name', 'description']);
11
+ }
12
+ catch (error) {
13
+ this.handleError(error);
14
+ }
15
+ }
16
+ }
17
+ ToolsList.description = 'List the current Respan MCP tools, their input schemas, and annotations.';
18
+ ToolsList.flags = {
19
+ ...BaseCommand.baseFlags,
20
+ 'mcp-url': Flags.string({ description: 'Custom MCP server URL (defaults to the active Platform or Enterprise server)' }),
21
+ };
22
+ export default ToolsList;
@@ -0,0 +1,38 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import type { AuthConfig } from './auth.js';
3
+ export declare function resolveMcpUrl(baseUrl: string, override?: string): URL;
4
+ export declare function withMcpClient<T>(auth: AuthConfig, override: string | undefined, run: (client: Client) => Promise<T>): Promise<T>;
5
+ export declare function listMcpTools(client: Client): Promise<{
6
+ inputSchema: {
7
+ [x: string]: unknown;
8
+ type: "object";
9
+ properties?: Record<string, object> | undefined;
10
+ required?: string[] | undefined;
11
+ };
12
+ name: string;
13
+ description?: string | undefined;
14
+ outputSchema?: {
15
+ [x: string]: unknown;
16
+ type: "object";
17
+ properties?: Record<string, object> | undefined;
18
+ required?: string[] | undefined;
19
+ } | undefined;
20
+ annotations?: {
21
+ title?: string | undefined;
22
+ readOnlyHint?: boolean | undefined;
23
+ destructiveHint?: boolean | undefined;
24
+ idempotentHint?: boolean | undefined;
25
+ openWorldHint?: boolean | undefined;
26
+ } | undefined;
27
+ execution?: {
28
+ taskSupport?: "optional" | "required" | "forbidden" | undefined;
29
+ } | undefined;
30
+ _meta?: Record<string, unknown> | undefined;
31
+ icons?: {
32
+ src: string;
33
+ mimeType?: string | undefined;
34
+ sizes?: string[] | undefined;
35
+ theme?: "light" | "dark" | undefined;
36
+ }[] | undefined;
37
+ title?: string | undefined;
38
+ }[]>;
@@ -0,0 +1,52 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
4
+ export function resolveMcpUrl(baseUrl, override) {
5
+ if (override) {
6
+ const url = new URL(override);
7
+ if (url.protocol !== 'https:' && !(url.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname))) {
8
+ throw new Error('MCP URL must use HTTPS (HTTP is allowed for localhost).');
9
+ }
10
+ if (url.username || url.password)
11
+ throw new Error('MCP URL must not contain credentials.');
12
+ return url;
13
+ }
14
+ const origin = new URL(baseUrl).origin;
15
+ if (origin === 'https://api.respan.ai')
16
+ return new URL('https://mcp.respan.ai/api/mcp');
17
+ if (origin === 'https://endpoint.respan.ai')
18
+ return new URL('https://mcp.respan.ai/api/mcp/enterprise');
19
+ throw new Error('For a custom API endpoint, specify its MCP server with --mcp-url.');
20
+ }
21
+ export async function withMcpClient(auth, override, run) {
22
+ const token = auth.apiKey || auth.accessToken;
23
+ if (!token)
24
+ throw new Error('No API key or access token available.');
25
+ const { version } = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8'));
26
+ const client = new Client({ name: 'respan-cli', version });
27
+ const transport = new StreamableHTTPClientTransport(resolveMcpUrl(auth.baseUrl, override), {
28
+ requestInit: { headers: { Authorization: `Bearer ${token}` } },
29
+ });
30
+ try {
31
+ await client.connect(transport);
32
+ return await run(client);
33
+ }
34
+ finally {
35
+ await client.close();
36
+ }
37
+ }
38
+ export async function listMcpTools(client) {
39
+ const tools = [];
40
+ const seen = new Set();
41
+ let cursor;
42
+ do {
43
+ const result = await client.listTools(cursor ? { cursor } : undefined);
44
+ tools.push(...result.tools);
45
+ cursor = result.nextCursor;
46
+ if (cursor && seen.has(cursor))
47
+ throw new Error('MCP server returned a repeated pagination cursor.');
48
+ if (cursor)
49
+ seen.add(cursor);
50
+ } while (cursor);
51
+ return tools;
52
+ }