Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a1438cb
initial mcp integration
STetsing Sep 4, 2025
e4060b6
adding remixmcpserver and matching user intends
Sep 9, 2025
75af8c4
adding default internal remix mcp server
STetsing Sep 11, 2025
aeb9544
connect to internal
STetsing Sep 11, 2025
131545a
remix mcp server working
STetsing Sep 15, 2025
7615397
adding tools events on interface'
STetsing Sep 15, 2025
6935c15
added tool execution
STetsing Sep 16, 2025
43cf3c2
tool call handled in streaming. recursive calls
STetsing Sep 16, 2025
4ac2ba9
fixing contract compilation tool
STetsing Sep 17, 2025
4b6721d
fully implementation of compilation and file handler
STetsing Sep 18, 2025
259017f
rm tests
STetsing Sep 22, 2025
2f3d79e
added compilation resourcess
STetsing Sep 22, 2025
414cfa3
mcp compile contract
STetsing Sep 24, 2025
9343229
minor
STetsing Sep 25, 2025
d5faf6c
Merge branch 'master' into mcp
STetsing Sep 25, 2025
7d018c8
resolved merging issues
STetsing Sep 25, 2025
784af84
adding deploymenthandler : fetchin accounts and balances
STetsing Sep 30, 2025
cc46733
fixing getting latest compilation result
STetsing Sep 30, 2025
d87189a
deployment ressources initial ipml
STetsing Oct 1, 2025
9d53303
Merge branch 'master' into mcp
STetsing Oct 7, 2025
89e2913
initial mcp e2e tests
STetsing Oct 8, 2025
e5e43af
disable resource caching
STetsing Oct 8, 2025
a616ed4
disable resource caching
STetsing Oct 8, 2025
436dc6d
disabled cahing on intelligent resource selection
STetsing Oct 8, 2025
57d1de8
mcp: deploy and interact
Oct 1, 2025
02195a8
implement other deployment tools
Oct 2, 2025
0729e76
fix deployment
Oct 2, 2025
52fc6d8
add audio prompt
Oct 2, 2025
773cf55
add debugger handler
Oct 7, 2025
6987377
fix validate param
Oct 7, 2025
b707e08
code analysis tools
Oct 7, 2025
503b3db
fix call
Oct 8, 2025
9f6081c
add runscript tool
Oct 8, 2025
a071c15
Merge pull request #6424 from remix-project-org/deploy_mcp
STetsing Oct 8, 2025
81d9313
max tool calls 10
STetsing Oct 9, 2025
e882e44
show diff on file content updated
STetsing Oct 9, 2025
53347a0
working integration tests
STetsing Oct 9, 2025
660d21d
working integration tests
STetsing Oct 9, 2025
8b75fa3
Merge branch 'master' into mcp
STetsing Oct 9, 2025
9e5ae0e
linting
STetsing Oct 9, 2025
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ apps/remixdesktop/log_input_signals_new.txt
logs
apps/remix-ide-e2e/src/extensions/chrome/metamask
apps/remix-ide-e2e/tmp/
apps/remix-ide-e2e/tmp/

# IDE - Cursor
.cursor/
.cursor/
344 changes: 344 additions & 0 deletions apps/remix-ide-e2e/src/tests/mcp_remix_server.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,344 @@
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'

const testContract = `
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract RemixMCPServerTest {
uint256 public testValue;
string public testString;

constructor(uint256 _value, string memory _str) {
testValue = _value;
testString = _str;
}

function updateValue(uint256 _newValue) public {
testValue = _newValue;
}

function updateString(string memory _newString) public {
testString = _newString;
}
}
`;

module.exports = {
'@disabled': false,
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},

'Should verify RemixMCPServer initialization': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remix-ai-assistant"]')
.execute(function () {
const aiPlugin = (window as any).getRemixAIPlugin;
if (!aiPlugin?.remixMCPServer) {
return { error: 'RemixMCPServer not available' };
}

const server = aiPlugin.remixMCPServer;
return {
hasRemixMcpServer: !!server,
serverName: server.serverName || null,
version: server.version || null,
isInitialized: !!server.tools && !!server.resources,
hasToolRegistry: !!server.tools,
hasResourceProviders: !!server.resources,
capabilities: server.getCapabilities() || null
};
}, [], function (result) {
const data = result.value as any;
if (data.error) {
console.error('RemixMCPServer error:', data.error);
return;
}
browser.assert.ok(data.hasRemixMcpServer, 'Should have RemixMCPServer instance');
browser.assert.ok(data.isInitialized, 'Server should be properly initialized');
browser.assert.ok(data.hasToolRegistry, 'Should have tool registry');
browser.assert.ok(data.hasResourceProviders, 'Should have resource providers');
});
},

'Should test RemixMCPServer tool registration': function (browser: NightwatchBrowser) {
browser
.execute(function () {
const aiPlugin = (window as any).getRemixAIPlugin;
if (!aiPlugin?.remixMCPServer?.tools) {
return { error: 'Tool registry not available' };
}

const allTools = aiPlugin.remixMCPServer.tools.list();
const compilationTools = allTools.filter((t: any) =>
t.name.includes('compile') || t.category === 'COMPILATION'
);

const deploymentTools = allTools.filter((t: any) =>
t.name.includes('deploy') || t.name.includes('account') || t.category === 'DEPLOYMENT'
);

const fileTools = allTools.filter((t: any) =>
t.name.includes('file') || t.category === 'FILE_SYSTEM'
);

return {
totalTools: allTools.length,
compilationToolCount: compilationTools.length,
deploymentToolCount: deploymentTools.length,
fileToolCount: fileTools.length,
sampleTools: allTools.slice(0, 3).map((t: any) => ({
name: t.name,
category: t.category,
hasHandler: !!t.handler
}))
};
}, [], function (result) {
const data = result.value as any;
if (data.error) {
console.error('Tool registry error:', data.error);
return;
}
browser.assert.ok(data.totalTools > 0, 'Should have registered tools');
browser.assert.ok(data.compilationToolCount > 0, 'Should have compilation tools');
browser.assert.ok(data.deploymentToolCount > 0, 'Should have deployment tools');
});
},

'Should test RemixMCPServer resource providers': function (browser: NightwatchBrowser) {
browser
.execute(function () {
const aiPlugin = (window as any).getRemixAIPlugin;
if (!aiPlugin?.remixMCPServer?.resources) {
return { error: 'Resource providers not available' };
}

const resourceProviders = aiPlugin.remixMCPServer.resources.providers;
const deploymentProvider = resourceProviders.get('deployment');
const projectProvider = resourceProviders.get('project');
const compilerProvider = resourceProviders.get('compiler');

return {
totalProviders: resourceProviders.size,
hasDeploymentProvider: !!deploymentProvider,
hasProjectProvider: !!projectProvider,
hasCompilerProvider: !!compilerProvider,
deploymentProviderMethods: deploymentProvider ? Object.getOwnPropertyNames(Object.getPrototypeOf(deploymentProvider)) : [],
projectProviderMethods: projectProvider ? Object.getOwnPropertyNames(Object.getPrototypeOf(projectProvider)) : []
};
}, [], function (result) {
const data = result.value as any;
if (data.error) {
console.error('Resource providers error:', data.error);
return;
}
browser.assert.ok(data.totalProviders > 0, 'Should have resource providers');
browser.assert.ok(data.hasDeploymentProvider, 'Should have deployment provider');
browser.assert.ok(data.hasProjectProvider, 'Should have project provider');
});
},

'Should test RemixMCPServer main resources reading via server': function (browser: NightwatchBrowser) {
browser
.execute(async function () {
const aiPlugin = (window as any).getRemixAIPlugin;
if (!aiPlugin?.remixMCPServer) {
return { error: 'RemixMCPServer not available' };
}

try {
const server = aiPlugin.remixMCPServer;
const historyResource = await server.handleMessage({method:'resources/read', params:{uri:'deployment://history'}, id:"rvhsdf"});
const structureResource = await server.handleMessage({method:'resources/read', params:{uri:'project://structure'}, id:"rvhsdf"});
const configResource = await server.handleMessage({method:'resources/read', params:{uri:'compiler://config'}, id:"rvhsdf"});
console.log('done hanfdling messages')

return {
historyRead: !!historyResource,
structureRead: !!structureResource,
configRead: !!configResource,
historyMimeType: historyResource?.mimeType || null,
structureMimeType: structureResource?.mimeType || null,
configMimeType: configResource?.mimeType || null,
historyHasContent: !!historyResource?.text,
structureHasContent: !!structureResource?.text,
configHasContent: !!configResource?.text
};
} catch (error) {
return { error: error.message };
}
}, [], function (result) {
const data = result.value as any;
if (data.error) {
console.error('Server resource reading error:', data.error);
return;
}
browser.assert.ok(data.historyRead, 'Should read deployment history resource');
browser.assert.ok(data.structureRead, 'Should read project structure resource');
browser.assert.ok(data.configRead, 'Should read compiler config resource');
});
},

'Should test RemixMCPServer capabilities and metadata': function (browser: NightwatchBrowser) {
browser
.execute(function () {
const aiPlugin = (window as any).getRemixAIPlugin;
if (!aiPlugin?.remixMCPServer) {
return { error: 'RemixMCPServer not available' };
}

const server = aiPlugin.remixMCPServer;

// Test server metadata and capabilities
const capabilities = server.getCapabilities() || {};
const serverInfo = {
name: server.serverName,
version: server.version,
capabilities: capabilities
};

// Test tool and resource listing capabilities
const toolList = server.tools.list()
const resourceList = server.resources.list()

return {
serverInfo,
hasCapabilities: Object.keys(capabilities).length > 0,
supportsTools: !!capabilities.tools,
supportsResources: !!capabilities.resources,
toolListAvailable: !!toolList,
resourceListAvailable: !!resourceList,
toolCount: toolList.length,
resourceCount: resourceList.length
};
}, [], function (result) {
const data = result.value as any;
if (data.error) {
console.error('Server capabilities error:', data.error);
return;
}
browser.assert.ok(data.hasCapabilities, 'Should have server capabilities');
browser.assert.ok(data.supportsTools, 'Should support tools');
browser.assert.ok(data.toolCount > 0, 'Should tools');
browser.assert.ok(data.resourceCount > 0, 'Should resources');
browser.assert.ok(data.supportsResources, 'Should support resources');
});
},

'Should test RemixMCPServer error handling invalid tool execution': function (browser: NightwatchBrowser) {
browser
.execute(async function () {
const aiPlugin = (window as any).getRemixAIPlugin;
if (!aiPlugin?.remixMCPServer) {
return { error: 'RemixMCPServer not available' };
}

try {
const server = aiPlugin.remixMCPServer;

let invalidToolResult;
try {
invalidToolResult = await server.executeTool({
name: 'non_existent_tool',
arguments: {}
});
} catch (error) {
invalidToolResult = { isError: true, content: [{ text: error.message }] };
}

let invalidResourceResult;
try {
invalidResourceResult = await server.readResource('invalid://resource');
} catch (error) {
invalidResourceResult = null;
}

let invalidArgsResult;
try {
invalidArgsResult = await server.executeTool({
name: 'solidity_compile',
arguments: {
runs: 99999 // Invalid: too high
}
});
} catch (error) {
invalidArgsResult = { isError: true, content: [{ text: error.message }] };
}

return {
invalidToolHandled: invalidToolResult?.isError === true,
invalidResourceHandled: invalidResourceResult === null,
invalidArgsHandled: invalidArgsResult?.isError === true,
systemStable: true,
invalidToolMessage: invalidToolResult?.content?.[0]?.text || 'No message',
invalidArgsMessage: invalidArgsResult?.content?.[0]?.text || 'No message'
};
} catch (error) {
return { error: error.message };
}
}, [], function (result) {
const data = result.value as any;
if (data.error) {
console.error('Server error handling test error:', data.error);
return;
}
browser.assert.ok(data.invalidToolHandled, 'Should handle invalid tools gracefully');
browser.assert.ok(data.invalidResourceHandled, 'Should handle invalid resources gracefully');
browser.assert.ok(data.invalidArgsHandled, 'Should handle invalid arguments gracefully');
browser.assert.ok(data.systemStable, 'System should remain stable after errors');
});
},

'Should test RemixMCPServer performance and caching': function (browser: NightwatchBrowser) {
browser
.execute(async function () {
const aiPlugin = (window as any).getRemixAIPlugin;
if (!aiPlugin?.remixMCPServer) {
return { error: 'RemixMCPServer not available' };
}

try {
const server = aiPlugin.remixMCPServer;
const startTime = Date.now();

// Test multiple operations for performance
const operations = await Promise.all([
server.readResource('deployment://history'),
server.readResource('project://structure'),
]);

const endTime = Date.now();
const totalTime = endTime - startTime;

// Test caching behavior
const cachingStart = Date.now();
const cachedResource1 = await server.readResource('deployment://history');
const cachedResource2 = await server.readResource('project://structure');
const cachingEnd = Date.now();
const cachingTime = cachingEnd - cachingStart;

return {
operationsCompleted: operations.length,
totalExecutionTime: totalTime,
averageOperationTime: totalTime / operations.length,
cachingTime,
allOperationsSucceeded: operations.every(op => !!op),
performanceAcceptable: totalTime < 1000, // Should complete within 5 seconds
cachingWorking: cachingTime < totalTime // Caching should be faster
};
} catch (error) {
return { error: error.message };
}
}, [], function (result) {
const data = result.value as any;
if (data.error) {
console.error('Performance test error:', data.error);
return;
}
browser.assert.ok(data.allOperationsSucceeded, 'All operations should succeed');
browser.assert.ok(data.performanceAcceptable, 'Performance should be acceptable');
browser.assert.equal(data.operationsCompleted, 5, 'Should complete all test operations');
});
}
};
Loading