@tiny-fish/cli 0.2.0-next.78 → 0.2.0-next.80

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 CHANGED
@@ -10,6 +10,22 @@ npm install -g @tiny-fish/cli
10
10
 
11
11
  Requires Node.js 24+.
12
12
 
13
+ ### Connect Claude Code
14
+
15
+ Add TinyFish, complete MCP OAuth, and start the interactive walkthrough with one command:
16
+
17
+ ```bash
18
+ npx -y @tiny-fish/cli@latest connect claude-code --launch
19
+ ```
20
+
21
+ One-command OAuth requires a Claude Code release whose `claude mcp` command supports `login`.
22
+ Update Claude Code if the command reports that support is unavailable; older versions require
23
+ manual authentication from `/mcp`.
24
+
25
+ The command replaces existing user- and local-scope TinyFish registrations. It intentionally
26
+ leaves project-scoped `.mcp.json` entries unchanged because those are shared repository
27
+ configuration.
28
+
13
29
  ## Authentication
14
30
 
15
31
  Get your API key from [agent.tinyfish.ai](https://agent.tinyfish.ai).
@@ -0,0 +1,7 @@
1
+ import { Command } from "commander";
2
+ export declare const DEFAULT_ONBOARDING_PROMPT: string;
3
+ export declare function connectClaudeCode(options: {
4
+ mcpUrl: string;
5
+ launch: boolean;
6
+ }): void;
7
+ export declare function registerConnect(program: Command): void;
@@ -0,0 +1,101 @@
1
+ import spawn from "cross-spawn";
2
+ import { errLine } from "../lib/output.js";
3
+ const DEFAULT_MCP_URL = "https://agent.tinyfish.ai/mcp";
4
+ const NON_INTERACTIVE_TIMEOUT_MS = 10_000;
5
+ const MCP_LOGIN_UNAVAILABLE_MESSAGE = "This Claude Code installation does not expose `claude mcp login`, which is required for " +
6
+ "one-command MCP authentication. Update Claude Code and retry, or authenticate TinyFish " +
7
+ "manually from /mcp.";
8
+ export const DEFAULT_ONBOARDING_PROMPT = "I just connected TinyFish. Call the guide_next_step tool and walk me through it one step at a " +
9
+ "time. Ask for my input and wait for my reply before every tool call.";
10
+ function requireNativeMcpLogin() {
11
+ const result = spawn.sync("claude", ["mcp", "--help"], {
12
+ encoding: "utf8",
13
+ timeout: NON_INTERACTIVE_TIMEOUT_MS,
14
+ });
15
+ if (result.error || result.status !== 0) {
16
+ throw new Error(MCP_LOGIN_UNAVAILABLE_MESSAGE, { cause: result.error });
17
+ }
18
+ if (!/^\s*login(?:\s|\[)/m.test(result.stdout ?? "")) {
19
+ throw new Error(MCP_LOGIN_UNAVAILABLE_MESSAGE);
20
+ }
21
+ }
22
+ function removeExistingRegistration(scope) {
23
+ const result = spawn.sync("claude", ["mcp", "remove", "tinyfish", "--scope", scope], {
24
+ encoding: "utf8",
25
+ timeout: NON_INTERACTIVE_TIMEOUT_MS,
26
+ });
27
+ if (result.status === 0)
28
+ return;
29
+ const details = result.stderr?.trim() || result.error?.message || "unknown error";
30
+ if (/No MCP server named "tinyfish"/i.test(details))
31
+ return;
32
+ if (result.error) {
33
+ throw new Error(`Could not remove existing ${scope} TinyFish registration: ${details}`, {
34
+ cause: result.error,
35
+ });
36
+ }
37
+ throw new Error(`Could not remove existing ${scope} TinyFish registration: ${details}`);
38
+ }
39
+ export function connectClaudeCode(options) {
40
+ requireNativeMcpLogin();
41
+ // Project scope is shared in .mcp.json; a user setup command must not rewrite it.
42
+ removeExistingRegistration("user");
43
+ removeExistingRegistration("local");
44
+ errLine("Adding TinyFish to Claude Code...");
45
+ const addResult = spawn.sync("claude", ["mcp", "add", "--scope", "user", "--transport", "http", "tinyfish", options.mcpUrl], { stdio: "inherit" });
46
+ if (addResult.error || addResult.status !== 0) {
47
+ throw new Error("Could not add TinyFish to Claude Code", { cause: addResult.error });
48
+ }
49
+ errLine("Signing in to TinyFish...");
50
+ const loginResult = spawn.sync("claude", ["mcp", "login", "tinyfish"], { stdio: "inherit" });
51
+ if (loginResult.error || loginResult.status !== 0) {
52
+ throw new Error("Could not authenticate TinyFish in Claude Code", {
53
+ cause: loginResult.error,
54
+ });
55
+ }
56
+ if (!options.launch) {
57
+ errLine("TinyFish is connected. Open Claude Code to start using it.");
58
+ return;
59
+ }
60
+ errLine("Starting the TinyFish walkthrough in Claude Code...");
61
+ const result = spawn.sync("claude", [DEFAULT_ONBOARDING_PROMPT], { stdio: "inherit" });
62
+ if (result.error) {
63
+ throw new Error("Could not launch Claude Code", { cause: result.error });
64
+ }
65
+ if (result.signal === "SIGINT" || result.signal === "SIGTERM")
66
+ return;
67
+ if (result.status !== 0) {
68
+ throw new Error(`Claude Code walkthrough exited with status ${result.status ?? "unknown"}`);
69
+ }
70
+ }
71
+ export function registerConnect(program) {
72
+ program
73
+ .command("connect")
74
+ .description("Connect TinyFish to an AI agent")
75
+ .argument("<client>", "Agent client to connect (claude-code)")
76
+ .option("--launch", "Launch the agent and start the TinyFish walkthrough")
77
+ .option("--url <mcpUrl>", "MCP endpoint override")
78
+ .action((client, options) => {
79
+ if (client !== "claude-code") {
80
+ throw new Error(`Unsupported client: ${client}. Supported client: claude-code`);
81
+ }
82
+ const mcpUrl = options.url ?? DEFAULT_MCP_URL;
83
+ if (mcpUrl.startsWith("-")) {
84
+ throw new Error(`Invalid --url value: ${mcpUrl}`);
85
+ }
86
+ let parsedUrl;
87
+ try {
88
+ parsedUrl = new URL(mcpUrl);
89
+ }
90
+ catch {
91
+ throw new Error(`Invalid --url value: ${mcpUrl}`);
92
+ }
93
+ if (!["http:", "https:"].includes(parsedUrl.protocol)) {
94
+ throw new Error(`Invalid --url value: ${mcpUrl}`);
95
+ }
96
+ connectClaudeCode({
97
+ mcpUrl,
98
+ launch: options.launch ?? false,
99
+ });
100
+ });
101
+ }
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ import { registerRun } from "./commands/run.js";
11
11
  import { registerRuns } from "./commands/runs.js";
12
12
  import { registerConfigureClaude } from "./commands/config-claude.js";
13
13
  import { registerSearch } from "./commands/search.js";
14
+ import { registerConnect } from "./commands/connect.js";
14
15
  const { version } = createRequire(import.meta.url)("../package.json");
15
16
  const program = new Command();
16
17
  program
@@ -27,6 +28,7 @@ program
27
28
  }
28
29
  });
29
30
  registerAuth(program);
31
+ registerConnect(program);
30
32
  registerConfigureClaude(program);
31
33
  registerBrowser(program);
32
34
  registerProfile(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiny-fish/cli",
3
- "version": "0.2.0-next.78",
3
+ "version": "0.2.0-next.80",
4
4
  "description": "TinyFish CLI — run web automations from your terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -35,11 +35,13 @@
35
35
  "@tiny-fish/sdk": "^0.0.11",
36
36
  "chrome-cookies-secure": "^3.0.2",
37
37
  "commander": "^12.0.0",
38
+ "cross-spawn": "^7.0.6",
38
39
  "globals": "^17.4.0",
39
40
  "zod": "^4.3.6"
40
41
  },
41
42
  "devDependencies": {
42
43
  "@eslint/js": "^10.0.1",
44
+ "@types/cross-spawn": "^6.0.6",
43
45
  "@types/node": "^20.0.0",
44
46
  "@typescript-eslint/eslint-plugin": "^8.57.2",
45
47
  "@typescript-eslint/parser": "^8.57.2",
@@ -53,7 +55,7 @@
53
55
  "vite": "7.3.5",
54
56
  "brace-expansion": "^5.0.7",
55
57
  "postcss": "8.5.10",
56
- "tar": "^7"
58
+ "tar": "^7.5.20"
57
59
  },
58
60
  "engines": {
59
61
  "node": ">=24.0.0"