11import * as readline from 'readline' ;
2+ import { execFile } from 'child_process' ;
23
34import { Command , flags } from '@oclif/command' ;
5+ import { delay } from '@httptoolkit/util' ;
46
57import { SERVER_VERSION } from '../constants' ;
68import { apiRequest } from '../api/bridge-client' ;
@@ -51,6 +53,61 @@ function operationsToMcpTools(operations: HtkOperation[]): any[] {
5153}
5254
5355const POLL_INTERVAL_MS = 5_000 ;
56+ const LAUNCH_TIMEOUT_MS = 30_000 ;
57+ const LAUNCH_POLL_MS = 500 ;
58+
59+ function launchDesktopApp ( ) : boolean {
60+ const exePath = process . env . HTK_DESKTOP_EXE ;
61+ if ( ! exePath ) return false ;
62+
63+ execFile ( exePath , [ ] , ( ) => { } ) ;
64+ return true ;
65+ }
66+
67+ async function startHttpToolkit (
68+ log : ( msg : string ) => void ,
69+ refreshOperations : ( ) => Promise < void >
70+ ) : Promise < { content : any [ ] ; isError ?: boolean } > {
71+ // Check if it's already running (maybe it just connected since the last poll)
72+ await refreshOperations ( ) ;
73+ if ( ( await apiRequest ( 'GET' , '/api/status' ) . catch ( ( ) => null ) ) ?. ready ) {
74+ await refreshOperations ( ) ;
75+ return {
76+ content : [ { type : 'text' , text : 'HTTP Toolkit is already running and ready.' } ]
77+ } ;
78+ }
79+
80+ log ( 'Launching HTTP Toolkit desktop app...' ) ;
81+ if ( ! launchDesktopApp ( ) ) {
82+ return {
83+ content : [ { type : 'text' , text : 'Cannot launch HTTP Toolkit: desktop app path not detected.' } ] ,
84+ isError : true
85+ } ;
86+ }
87+
88+ // Wait for the UI to connect and send operations
89+ const deadline = Date . now ( ) + LAUNCH_TIMEOUT_MS ;
90+ while ( Date . now ( ) < deadline ) {
91+ await delay ( LAUNCH_POLL_MS ) ;
92+ await refreshOperations ( ) ;
93+ try {
94+ const status = await apiRequest ( 'GET' , '/api/status' ) ;
95+ if ( status ?. ready ) {
96+ log ( 'HTTP Toolkit is ready' ) ;
97+ return {
98+ content : [ { type : 'text' , text : 'HTTP Toolkit has been launched and is ready.' } ]
99+ } ;
100+ }
101+ } catch {
102+ // Server not yet available, keep waiting
103+ }
104+ }
105+
106+ return {
107+ content : [ { type : 'text' , text : 'HTTP Toolkit was launched but is not yet ready. It may still be starting up — try again in a moment.' } ] ,
108+ isError : true
109+ } ;
110+ }
54111
55112async function runMcpServer ( ) : Promise < void > {
56113 const log = ( msg : string ) => process . stderr . write ( `[MCP] ${ msg } \n` ) ;
@@ -68,20 +125,18 @@ async function runMcpServer(): Promise<void> {
68125
69126 function getToolsList ( ) : any [ ] {
70127 if ( cachedOperations . length > 0 ) return operationsToMcpTools ( cachedOperations ) ;
71- // No running instance — offer a placeholder tool
128+ // No running instance — the only available action is to launch it.
129+ // This tool disappears once HTTP Toolkit is running and real tools become available.
72130 return [ {
73131 name : 'start_httptoolkit' ,
74- description : 'HTTP Toolkit is not running or the UI is not connected. Please start HTTP Toolkit and try again .' ,
132+ description : 'HTTP Toolkit is not currently running. Call this to launch it — once started, more tools will become available .' ,
75133 inputSchema : { type : 'object' , properties : { } }
76134 } ] ;
77135 }
78136
79137 async function handleToolCall ( name : string , args : Record < string , unknown > ) : Promise < { content : any [ ] ; isError ?: boolean } > {
80138 if ( name === 'start_httptoolkit' ) {
81- return {
82- content : [ { type : 'text' , text : 'HTTP Toolkit is not running or the UI is not connected. Please start HTTP Toolkit and try again.' } ] ,
83- isError : true
84- } ;
139+ return startHttpToolkit ( log , refreshOperations ) ;
85140 }
86141
87142 // Map MCP tool name back to operation name
0 commit comments