Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion apps/remix-ide/src/app/udapp/run-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ const profile = {
'resolveContractAndAddInstance',
'showPluginDetails',
'getRunTabAPI',
'getDeployedContracts'
'getDeployedContracts',
'getAllDeployedInstances',
'setAccount'
]
}

Expand Down Expand Up @@ -151,6 +153,14 @@ export class RunTab extends ViewPlugin {
}
}

setAccount(address: string) {
this.emit('setAccountReducer', address)
}

getAllDeployedInstances() {
return this.REACT_API.instances?.instanceList
}

clearAllInstances() {
this.emit('clearAllInstancesReducer')
this.transactionHistory.clear()
Expand Down
13 changes: 8 additions & 5 deletions apps/remix-ide/src/blockchain/blockchain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const profile = {
name: 'blockchain',
displayName: 'Blockchain',
description: 'Blockchain - Logic',
methods: ['dumpState', 'getCode', 'getTransactionReceipt', 'addProvider', 'removeProvider', 'getCurrentFork', 'isSmartAccount', 'getAccounts', 'web3VM', 'web3', 'getProvider', 'getCurrentProvider', 'getCurrentNetworkStatus', 'getCurrentNetworkCurrency', 'getAllProviders', 'getPinnedProviders', 'changeExecutionContext', 'getProviderObject', 'runTx', 'getBalanceInEther', 'getCurrentProvider'],
methods: ['dumpState', 'getCode', 'getTransactionReceipt', 'addProvider', 'removeProvider', 'getCurrentFork', 'isSmartAccount', 'getAccounts', 'web3VM', 'web3', 'getProvider', 'getCurrentProvider', 'getCurrentNetworkStatus', 'getCurrentNetworkCurrency', 'getAllProviders', 'getPinnedProviders', 'changeExecutionContext', 'getProviderObject', 'runTx', 'getBalanceInEther', 'getCurrentProvider', 'deployContractAndLibraries', 'runOrCallContractMethod'],

version: packageJson.version
}
Expand Down Expand Up @@ -303,7 +303,9 @@ export class Blockchain extends Plugin {
args,
(error, data) => {
if (error) {
return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error.error ? error.error : error}`)
statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error.error ? error.error : error}`)
finalCb(error)
return
}

statusCb(`creation of ${selectedContract.name} pending...`)
Expand Down Expand Up @@ -546,7 +548,7 @@ export class Blockchain extends Plugin {
if (txResult.receipt.status === false || txResult.receipt.status === '0x0' || txResult.receipt.status === 0) {
return finalCb(`creation of ${selectedContract.name} errored: transaction execution failed`)
}
finalCb(null, selectedContract, address)
finalCb(null, selectedContract, address, txResult)
})
}

Expand Down Expand Up @@ -607,7 +609,7 @@ export class Blockchain extends Plugin {
}

changeExecutionContext(context, confirmCb, infoCb, cb) {
if (this.currentRequest && this.currentRequest.from && !this.currentRequest.from.startsWith('injected')) {
if (this.currentRequest && this.currentRequest.from && !this.currentRequest.from.startsWith('injected') && this.currentRequest.from !== 'remixAI') {
// only injected provider can update the provider.
return
}
Expand Down Expand Up @@ -668,7 +670,7 @@ export class Blockchain extends Plugin {
return txlistener
}

runOrCallContractMethod(contractName, contractAbi, funABI, contract, value, address, callType, lookupOnly, logMsg, logCallback, outputCb, confirmationCb, continueCb, promptCb) {
runOrCallContractMethod(contractName, contractAbi, funABI, contract, value, address, callType, lookupOnly, logMsg, logCallback, outputCb, confirmationCb, continueCb, promptCb, finalCb) {
// contractsDetails is used to resolve libraries
txFormat.buildData(
contractName,
Expand Down Expand Up @@ -701,6 +703,7 @@ export class Blockchain extends Plugin {
if (lookupOnly) {
outputCb(returnValue)
}
if (finalCb) finalCb(error, {txResult, address: _address, returnValue})
})
},
(msg) => {
Expand Down
12 changes: 9 additions & 3 deletions libs/remix-ai-core/src/remix-mcp-server/RemixMCPServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { createCompilationTools } from './handlers/CompilationHandler';
import { createFileManagementTools } from './handlers/FileManagementHandler';
import { createDeploymentTools } from './handlers/DeploymentHandler';
import { createDebuggingTools } from './handlers/DebuggingHandler';
import { createCodeAnalysisTools } from './handlers/CodeAnalysisHandler';

// Import resource providers
import { ProjectResourceProvider } from './providers/ProjectResourceProvider';
Expand Down Expand Up @@ -464,9 +465,14 @@ export class RemixMCPServer extends EventEmitter implements IRemixMCPServer {
console.log(`Registered ${deploymentTools.length} deployment tools`, 'info');

// Register debugging tools
// const debuggingTools = createDebuggingTools();
// this._tools.registerBatch(debuggingTools);
// console.log(`Registered ${debuggingTools.length} debugging tools`, 'info');
const debuggingTools = createDebuggingTools();
this._tools.registerBatch(debuggingTools);
console.log(`Registered ${debuggingTools.length} debugging tools`, 'info');

// Register debugging tools
const codeAnalysisTools = createCodeAnalysisTools();
this._tools.registerBatch(codeAnalysisTools);
console.log(`Registered ${codeAnalysisTools.length} code analysis tools`, 'info');

const totalTools = this._tools.list().length;
console.log(`Total tools registered: ${totalTools}`, 'info');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/**
* Code Analysis Tool Handlers for Remix MCP Server
*/

import { IMCPToolResult } from '../../types/mcp';
import { BaseToolHandler } from '../registry/RemixToolRegistry';
import {
ToolCategory,
RemixToolDefinition
} from '../types/mcpTools';
import { Plugin } from '@remixproject/engine';
import { performSolidityScan } from '@remix-project/core-plugin';

/**
* Solidity Scan Tool Handler
* Analyzes Solidity code for security vulnerabilities and code quality issues
*/
export class SolidityScanHandler extends BaseToolHandler {
name = 'solidity_scan';
description = 'Scan Solidity smart contracts for security vulnerabilities and code quality issues using SolidityScan API';
inputSchema = {
type: 'object',
properties: {
filePath: {
type: 'string',
description: 'Path to the Solidity file to scan (relative to workspace root)'
}
},
required: ['filePath']
};

getPermissions(): string[] {
return ['analysis:scan', 'file:read'];
}

validate(args: { filePath: string }): boolean | string {
const required = this.validateRequired(args, ['filePath']);
if (required !== true) return required;

const types = this.validateTypes(args, {
filePath: 'string'
});
if (types !== true) return types;

if (!args.filePath.endsWith('.sol')) {
return 'File must be a Solidity file (.sol)';
}

return true;
}

async execute(args: { filePath: string }, plugin: Plugin): Promise<IMCPToolResult> {
try {
// Check if file exists
const workspace = await plugin.call('filePanel', 'getCurrentWorkspace');
const fileName = `${workspace.name}/${args.filePath}`;
const filePath = `.workspaces/${fileName}`;

const exists = await plugin.call('fileManager', 'exists', filePath);
if (!exists) {
return this.createErrorResult(`File not found: ${args.filePath}`);
}

// Use the core scanning function from remix-core-plugin
const scanReport = await performSolidityScan(plugin, args.filePath);

// Process scan results into structured format
const findings = [];

for (const template of scanReport.multi_file_scan_details || []) {
if (template.metric_wise_aggregated_findings?.length) {
for (const details of template.metric_wise_aggregated_findings) {
for (const finding of details.findings) {
findings.push({
metric: details.metric_name,
severity: details.severity || 'unknown',
title: finding.title || details.metric_name,
description: finding.description || details.description,
lineStart: finding.line_nos_start?.[0],
lineEnd: finding.line_nos_end?.[0],
file: template.file_name,
recommendation: finding.recommendation
});
}
}
}
}

const result = {
success: true,
fileName,
scanCompletedAt: new Date().toISOString(),
totalFindings: findings.length,
findings,
summary: {
critical: findings.filter(f => f.severity === 'critical').length,
high: findings.filter(f => f.severity === 'high').length,
medium: findings.filter(f => f.severity === 'medium').length,
low: findings.filter(f => f.severity === 'low').length,
informational: findings.filter(f => f.severity === 'informational').length
}
};

return this.createSuccessResult(result);

} catch (error) {
return this.createErrorResult(`Scan failed: ${error.message}`);
}
}
}

/**
* Create code analysis tool definitions
*/
export function createCodeAnalysisTools(): RemixToolDefinition[] {
return [
{
name: 'solidity_scan',
description: 'Scan Solidity smart contracts for security vulnerabilities and code quality issues using SolidityScan API',
inputSchema: new SolidityScanHandler().inputSchema,
category: ToolCategory.ANALYSIS,
permissions: ['analysis:scan', 'file:read'],
handler: new SolidityScanHandler()
}
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,49 +30,35 @@ export class StartDebugSessionHandler extends BaseToolHandler {
inputSchema = {
type: 'object',
properties: {
contractAddress: {
type: 'string',
description: 'Contract address to debug',
pattern: '^0x[a-fA-F0-9]{40}$'
},
transactionHash: {
type: 'string',
description: 'Transaction hash to debug (optional)',
pattern: '^0x[a-fA-F0-9]{64}$'
},
sourceFile: {
type: 'string',
description: 'Source file path to debug'
},
/*
network: {
type: 'string',
description: 'Network to debug on',
default: 'local'
}
*/
},
required: ['contractAddress']
required: ['transactionHash']
};

getPermissions(): string[] {
return ['debug:start'];
}

validate(args: DebugSessionArgs): boolean | string {
const required = this.validateRequired(args, ['contractAddress']);
const required = this.validateRequired(args, ['transactionHash']);
if (required !== true) return required;

const types = this.validateTypes(args, {
contractAddress: 'string',
transactionHash: 'string',
sourceFile: 'string',
network: 'string'
});
if (types !== true) return types;

if (!args.contractAddress.match(/^0x[a-fA-F0-9]{40}$/)) {
return 'Invalid contract address format';
}

if (args.transactionHash && !args.transactionHash.match(/^0x[a-fA-F0-9]{64}$/)) {
return 'Invalid transaction hash format';
}
Expand All @@ -82,21 +68,15 @@ export class StartDebugSessionHandler extends BaseToolHandler {

async execute(args: DebugSessionArgs, plugin: Plugin): Promise<IMCPToolResult> {
try {
// TODO: Integrate with Remix debugger plugin
const sessionId = 'debug_' + Date.now();

await plugin.call('debugger', 'debug', args.transactionHash)
// Mock debug session creation
const result: DebugSessionResult = {
success: true,
sessionId,
contractAddress: args.contractAddress,
network: args.network || 'local',
transactionHash: args.transactionHash,
sourceFile: args.sourceFile,
status: 'started',
createdAt: new Date().toISOString()
};

plugin.call('menuicons', 'select', 'debugger')
return this.createSuccessResult(result);

} catch (error) {
Expand Down
Loading