@@ -57,11 +57,11 @@ inspectCorpusTransactionsTool args env _ _ = do
5757 let corpusList = Set. toList c
5858 startIndex = (page - 1 ) * pageSize
5959 pageItems = take pageSize $ drop startIndex corpusList
60-
61- ppSequence (i, txs) =
60+
61+ ppSequence (i, txs) =
6262 printf " Sequence (value: %d):\n %s" i (unlines $ map (ppTx Map. empty) txs)
6363
64- return $ if null pageItems
64+ return $ if null pageItems
6565 then " No more transactions found."
6666 else intercalate " \n " (map ppSequence pageItems)
6767 where
@@ -81,12 +81,28 @@ splitOn c s = case break (== c) s of
8181 (_: r) -> splitOn c r
8282
8383parseArg :: String -> Maybe AbiValue
84- parseArg s =
84+ parseArg s =
8585 let s' = trim s
8686 in if " 0x" `isPrefixOf` s'
8787 then AbiAddress . fromIntegral <$> (readMaybe s' :: Maybe Integer )
8888 else AbiUInt 256 . fromIntegral <$> (readMaybe s' :: Maybe Integer )
8989
90+ parseFuzzArg :: String -> Maybe (Maybe AbiValue )
91+ parseFuzzArg s =
92+ let s' = trim s
93+ in if s' == " ?"
94+ then Just Nothing
95+ else Just <$> parseArg s'
96+
97+ parseFuzzCall :: String -> Maybe (Text , [Maybe AbiValue ])
98+ parseFuzzCall s = do
99+ let (fname, rest) = break (== ' (' ) s
100+ if null rest then Nothing else do
101+ let argsS = take (length rest - 2 ) (drop 1 rest) -- remove parens
102+ let argParts = if all isSpace argsS then [] else splitOn ' ,' argsS
103+ args <- mapM parseFuzzArg argParts
104+ return (pack fname, args)
105+
90106parseCall :: String -> Maybe (String , [AbiValue ])
91107parseCall s = do
92108 let (fname, rest) = break (== ' (' ) s
@@ -126,10 +142,10 @@ injectTransactionTool args env bus _ = do
126142 Just p -> Data.Maybe. fromMaybe 0 (readMaybe (unpack p))
127143 Nothing -> 0
128144 txStr = maybe " " unpack (lookup " transaction" args)
129-
145+
130146 c <- readIORef env. corpusRef
131147 let corpusList = Set. toList c
132-
148+
133149 if idx < 0 || idx >= length corpusList
134150 then return " Error: Invalid sequence index."
135151 else do
@@ -139,16 +155,16 @@ injectTransactionTool args env bus _ = do
139155 else do
140156 let contextTx = case originalSeq of
141157 [] -> Nothing
142- (x: xs) -> Just (if pos > 0 && pos <= length (x: xs)
143- then (x: xs) !! (pos - 1 )
158+ (x: xs) -> Just (if pos > 0 && pos <= length (x: xs)
159+ then (x: xs) !! (pos - 1 )
144160 else x)
145161 case parseTx contextTx txStr of
146162 Nothing -> return " Error: Failed to parse transaction string."
147163 Just newTx -> do
148164 let newSeq = take pos originalSeq ++ [newTx]
149165 replyVar <- newEmptyTMVarIO
150166 atomically $ writeTChan bus (WrappedMessage AIId (ToFuzzer 0 (ExecuteSequence newSeq (Just replyVar))))
151-
167+
152168 -- Wait for reply
153169 found <- atomically $ takeTMVar replyVar
154170 if found
@@ -163,13 +179,16 @@ dumpLcovTool _ env _ _ = do
163179 filename <- saveLcovHook env dir env. sourceCache contracts
164180 return $ " Dumped LCOV coverage to " ++ filename
165181
166- -- | Implementation of prioritize_function tool
167- prioritizeFunctionTool :: ToolExecution
168- prioritizeFunctionTool args env bus _ = do
169- let msg = Data.Maybe. fromMaybe " " (lookup " function" args)
170- let nWorkers = getNFuzzWorkers env. cfg. campaignConf
171- mapM_ (\ i -> atomically $ writeTChan bus (WrappedMessage AIId (ToFuzzer i (PrioritizeFunction (unpack msg))))) [0 .. nWorkers - 1 ]
172- return $ printf " Requested prioritization of function '%s' on %d fuzzers" (unpack msg) nWorkers
182+ -- | Implementation of fuzz_transaction tool
183+ fuzzTransactionTool :: ToolExecution
184+ fuzzTransactionTool args env bus _ = do
185+ let txStr = Data.Maybe. fromMaybe " " (lookup " transaction" args)
186+ case parseFuzzCall (unpack txStr) of
187+ Nothing -> return " Error: Failed to parse transaction string."
188+ Just (fname, fuzzArgs) -> do
189+ let nWorkers = getNFuzzWorkers env. cfg. campaignConf
190+ mapM_ (\ i -> atomically $ writeTChan bus (WrappedMessage AIId (ToFuzzer i (FuzzTransaction fname fuzzArgs)))) [0 .. nWorkers - 1 ]
191+ return $ printf " Requested fuzzing of transaction '%s' on %d fuzzers" (unpack txStr) nWorkers
173192
174193-- | Implementation of clear_priorities tool
175194clearPrioritiesTool :: ToolExecution
@@ -182,7 +201,11 @@ clearPrioritiesTool _ env bus _ = do
182201readLogsTool :: ToolExecution
183202readLogsTool _ _ _ logsRef = do
184203 logs <- readIORef logsRef
185- return $ unpack $ T. unlines $ reverse logs
204+ -- Get last 100 logs
205+ -- logs is [Newest, ..., Oldest]
206+ -- We want to take the 100 newest, and show them in chronological order
207+ let logsToShow = reverse $ take 100 logs
208+ return $ unpack $ T. unlines $ logsToShow
186209
187210-- | Implementation of show_coverage tool
188211showCoverageTool :: ToolExecution
@@ -235,7 +258,7 @@ availableTools =
235258 , Tool " inspect_corpus_transactions" " Browse the corpus transactions" inspectCorpusTransactionsTool
236259 , Tool " inject_transaction" " Inject a transaction into a sequence and execute it" injectTransactionTool
237260 , Tool " dump_lcov" " Dump coverage in LCOV format" dumpLcovTool
238- , Tool " prioritize_function " " Prioritize a function for fuzzing " prioritizeFunctionTool
261+ , Tool " fuzz_transaction " " Fuzz a transaction with optional concrete arguments " fuzzTransactionTool
239262 , Tool " clear_priorities" " Clear the function prioritization list" clearPrioritiesTool
240263 , Tool " read_logs" " Read the last 100 log messages" readLogsTool
241264 , Tool " show_coverage" " Show coverage report for a particular contract" showCoverageTool
@@ -254,7 +277,7 @@ runMCPServer env port logsRef = do
254277 let serverInfo = McpServerInfo
255278 { serverName = " Echidna MCP Server"
256279 , serverVersion = " 1.0.0"
257- , serverInstructions = " Echidna Agent Interface. Available tools: read_corpus, inspect_corpus_transactions, dump_lcov, prioritize_function , clear_priorities, read_logs, show_coverage"
280+ , serverInstructions = " Echidna Agent Interface. Available tools: read_corpus, inspect_corpus_transactions, dump_lcov, fuzz_transaction , clear_priorities, read_logs, show_coverage"
258281 }
259282
260283 let mkToolDefinition :: Tool -> ToolDefinition
@@ -267,7 +290,7 @@ runMCPServer env port logsRef = do
267290 , required = [" page" ]
268291 }
269292 " inject_transaction" -> InputSchemaDefinitionObject
270- { properties =
293+ { properties =
271294 [ (" sequence_index" , InputSchemaDefinitionProperty " string" " The index of the sequence in the corpus" )
272295 , (" position" , InputSchemaDefinitionProperty " string" " The position to insert the transaction at" )
273296 , (" transaction" , InputSchemaDefinitionProperty " string" " The transaction string (e.g. 'func(arg1, arg2)')" )
@@ -278,9 +301,9 @@ runMCPServer env port logsRef = do
278301 { properties = []
279302 , required = []
280303 }
281- " prioritize_function " -> InputSchemaDefinitionObject
282- { properties = [(" function " , InputSchemaDefinitionProperty " string" " The name of the function to prioritize " )]
283- , required = [" function " ]
304+ " fuzz_transaction " -> InputSchemaDefinitionObject
305+ { properties = [(" transaction " , InputSchemaDefinitionProperty " string" " The transaction string (e.g. 'func(arg1, ?, arg3)') " )]
306+ , required = [" transaction " ]
284307 }
285308 " clear_priorities" -> InputSchemaDefinitionObject
286309 { properties = []
0 commit comments