Skip to content

Commit 25439ed

Browse files
committed
initial mcp code integration
1 parent a668544 commit 25439ed

File tree

4 files changed

+228
-25
lines changed

4 files changed

+228
-25
lines changed

libs/remix-ai-core/src/inferencers/mcp/mcpInferencer.ts

Lines changed: 172 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { ResourceScoring } from "../../services/resourceScoring";
1919
import { RemixMCPServer } from '@remix/remix-ai-core';
2020
import { endpointUrls } from "@remix-endpoints-helper"
2121
import { text } from "stream/consumers";
22+
import { CodeExecutor } from "./codeExecutor";
23+
import { ToolApiGenerator } from "./toolApiGenerator";
2224

2325
// Helper function to track events using MatomoManager instance
2426
function trackMatomoEvent(category: string, action: string, name?: string) {
@@ -1362,29 +1364,51 @@ export class MCPInferencer extends RemoteInferencer implements ICompletions, IGe
13621364
async getToolsForLLMRequest(provider?: string): Promise<any[]> {
13631365
const mcpTools = await this.getAvailableToolsForLLM();
13641366

1365-
// Format tools based on provider
1366-
let convertedTools: any[];
1367+
if (mcpTools.length === 0) {
1368+
return [];
1369+
}
1370+
1371+
// Generate compact tool descriptions
1372+
const apiGenerator = new ToolApiGenerator();
1373+
const apiDescription = apiGenerator.generateAPIDescription();
1374+
const toolsList = apiGenerator.generateToolsList(mcpTools);
1375+
1376+
console.log('toolsList', toolsList);
13671377

1378+
// Create single execute_tool with TypeScript API definitions
1379+
const executeToolDef = {
1380+
name: "execute_tool",
1381+
description: `Execute TypeScript code to interact with the Remix IDE API.
1382+
1383+
${apiDescription}
1384+
1385+
${toolsList}`,
1386+
input_schema: {
1387+
type: "object",
1388+
properties: {
1389+
code: {
1390+
type: "string",
1391+
description: "TypeScript code to execute. Use callMCPTool(toolName, args) to call available tools."
1392+
}
1393+
},
1394+
required: ["code"]
1395+
}
1396+
};
1397+
1398+
// Format based on provider
13681399
if (provider === 'anthropic') {
1369-
// Anthropic format: direct object with name, description, input_schema
1370-
convertedTools = mcpTools.map(tool => ({
1371-
name: tool.name,
1372-
description: tool.description,
1373-
input_schema: tool.inputSchema
1374-
}));
1400+
return [executeToolDef];
13751401
} else {
1376-
// OpenAI and other providers format: type + function wrapper
1377-
convertedTools = mcpTools.map(tool => ({
1402+
// OpenAI and other providers format
1403+
return [{
13781404
type: "function",
13791405
function: {
1380-
name: tool.name,
1381-
description: tool.description,
1382-
parameters: tool.inputSchema
1406+
name: executeToolDef.name,
1407+
description: executeToolDef.description,
1408+
parameters: executeToolDef.input_schema
13831409
}
1384-
}));
1410+
}];
13851411
}
1386-
1387-
return convertedTools;
13881412
}
13891413

13901414
convertLLMToolCallToMCP(llmToolCall: any): IMCPToolCall {
@@ -1413,7 +1437,137 @@ export class MCPInferencer extends RemoteInferencer implements ICompletions, IGe
14131437
* Execute a tool call from the LLM
14141438
*/
14151439
async executeToolForLLM(toolCall: IMCPToolCall): Promise<IMCPToolResult> {
1416-
// Find which server has this tool
1440+
// Handle code execution mode
1441+
if (toolCall.name === 'execute_tool') {
1442+
const code = toolCall.arguments?.code;
1443+
if (!code || typeof code !== 'string') {
1444+
throw new Error('execute_tool requires a code argument');
1445+
}
1446+
1447+
// Create code executor with callback to execute actual MCP tools
1448+
const codeExecutor = new CodeExecutor(
1449+
async (innerToolCall: IMCPToolCall) => {
1450+
// Find which server has this tool
1451+
const toolsFromServers = await this.getAllTools();
1452+
let targetServer: string | undefined;
1453+
1454+
for (const [serverName, tools] of Object.entries(toolsFromServers)) {
1455+
if (tools.some(tool => tool.name === innerToolCall.name)) {
1456+
targetServer = serverName;
1457+
break;
1458+
}
1459+
}
1460+
1461+
if (!targetServer) {
1462+
throw new Error(`Tool '${innerToolCall.name}' not found in any connected MCP server`);
1463+
}
1464+
1465+
console.log(`[MCP Code Mode] Executing tool ${innerToolCall.name} from server ${targetServer}`);
1466+
const result = await this.executeTool(targetServer, innerToolCall);
1467+
console.log(`[MCP Code Mode] inner tool ${innerToolCall.name} produced result`)
1468+
console.log(result)
1469+
return result
1470+
},
1471+
30000 // 30 second timeout
1472+
);
1473+
1474+
// Execute the code
1475+
const result = await codeExecutor.execute(code);
1476+
console.log(`[MCP Code Mode] inner tool executed with result`, result);
1477+
1478+
// Convert code execution result to MCP tool result format
1479+
if (result.success) {
1480+
const content = [];
1481+
1482+
// Add all tool call results with their full payloads
1483+
if (result.toolCallRecords && result.toolCallRecords.length > 0) {
1484+
content.push({
1485+
type: 'text' as const,
1486+
text: `Tool Calls (${result.toolCallRecords.length}):`
1487+
});
1488+
1489+
for (const record of result.toolCallRecords) {
1490+
const toolResult = record.result.content
1491+
.map((c: any) => c.text || JSON.stringify(c))
1492+
.join('\n');
1493+
1494+
content.push({
1495+
type: 'text' as const,
1496+
text: `\n[${record.name}] (${record.executionTime}ms)\nArguments: ${JSON.stringify(record.arguments, null, 2)}\nResult:\n${toolResult}`
1497+
});
1498+
}
1499+
}
1500+
1501+
if (result.output) {
1502+
content.push({
1503+
type: 'text' as const,
1504+
text: `\nConsole Output:\n${result.output}`
1505+
});
1506+
}
1507+
1508+
if (result.returnValue !== undefined) {
1509+
content.push({
1510+
type: 'text' as const,
1511+
text: `\nReturn Value:\n${JSON.stringify(result.returnValue, null, 2)}`
1512+
});
1513+
}
1514+
1515+
content.push({
1516+
type: 'text' as const,
1517+
text: `\nExecution Stats:\n- Time: ${result.executionTime}ms\n- Tools Called: ${result.toolsCalled.join(', ') || 'none'}`
1518+
});
1519+
1520+
return {
1521+
content: content.length > 0 ? content : [{ type: 'text', text: 'Code executed successfully with no output' }],
1522+
isError: false
1523+
};
1524+
} else {
1525+
const content = [];
1526+
1527+
content.push({
1528+
type: 'text' as const,
1529+
text: `Code Execution Error:\n${result.error}`
1530+
});
1531+
1532+
// Include tool call results even on error
1533+
if (result.toolCallRecords && result.toolCallRecords.length > 0) {
1534+
content.push({
1535+
type: 'text' as const,
1536+
text: `\nTool Calls Before Error (${result.toolCallRecords.length}):`
1537+
});
1538+
1539+
for (const record of result.toolCallRecords) {
1540+
const toolResult = record.result.content
1541+
.map((c: any) => c.text || JSON.stringify(c))
1542+
.join('\n');
1543+
1544+
content.push({
1545+
type: 'text' as const,
1546+
text: `\n[${record.name}] (${record.executionTime}ms)\nArguments: ${JSON.stringify(record.arguments, null, 2)}\nResult:\n${toolResult}`
1547+
});
1548+
}
1549+
}
1550+
1551+
if (result.output) {
1552+
content.push({
1553+
type: 'text' as const,
1554+
text: `\nConsole Output:\n${result.output}`
1555+
});
1556+
}
1557+
1558+
content.push({
1559+
type: 'text' as const,
1560+
text: `\nExecution Time: ${result.executionTime}ms`
1561+
});
1562+
1563+
return {
1564+
content,
1565+
isError: true
1566+
};
1567+
}
1568+
}
1569+
1570+
// Fallback: Legacy direct tool execution (should not be reached with code mode)
14171571
const toolsFromServers = await this.getAllTools();
14181572
let targetServer: string | undefined;
14191573

@@ -1427,7 +1581,7 @@ export class MCPInferencer extends RemoteInferencer implements ICompletions, IGe
14271581
if (!targetServer) {
14281582
throw new Error(`Tool '${toolCall.name}' not found in any connected MCP server`);
14291583
}
1430-
console.log(`executing tool ${toolCall.name} from server ${targetServer}`)
1584+
console.log(`[MCP Legacy Mode] Executing tool ${toolCall.name} from server ${targetServer}`)
14311585
return this.executeTool(targetServer, toolCall);
14321586
}
14331587

libs/remix-ai-core/src/inferencers/remote/remoteInference.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ export class RemoteInferencer implements ICompletions, IGeneration {
2424
protected sanitizePromptByteSize(prompt: string, provider?: string): string {
2525
// Provider-specific max byte limits
2626
const providerLimits: Record<string, number> = {
27-
'mistralai': 30000,
28-
'anthropic': 40000,
29-
'openai': 40000
27+
'mistralai': 70000,
28+
'anthropic': 70000,
29+
'openai': 70000
3030
};
3131

3232
// Get max bytes based on provider, default to 50KB

libs/remix-ai-core/src/remix-mcp-server/handlers/DeploymentHandler.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,13 @@ export class DeployContractHandler extends BaseToolHandler {
9898

9999
async execute(args: DeployContractArgs, plugin: Plugin): Promise<IMCPToolResult> {
100100
try {
101-
102-
await plugin.call('sidePanel', 'showContent', 'udapp' )
103-
101+
console.log("Executing deploy_contract with", args.file)
104102
// Get compilation result to find contract
105103
const compilerAbstract = await plugin.call('compilerArtefacts', 'getCompilerAbstract', args.file) as any;
106104
const data = getContractData(args.contractName, compilerAbstract)
107105
if (!data) {
106+
console.log("compilerAbstract", compilerAbstract)
107+
console.log("data", data)
108108
return this.createErrorResult(`Could not retrieve contract data for '${args.contractName}'`);
109109
}
110110

@@ -136,6 +136,7 @@ export class DeployContractHandler extends BaseToolHandler {
136136
}
137137

138138
const receipt = (txReturn.txResult.receipt)
139+
console.log("receipt is", receipt)
139140
const result: DeploymentResult = {
140141
transactionHash: receipt.hash,
141142
gasUsed: toNumber(receipt.gasUsed),
@@ -145,7 +146,7 @@ export class DeployContractHandler extends BaseToolHandler {
145146
contractAddress: receipt.contractAddress,
146147
success: receipt.status === 1 ? true : false
147148
};
148-
149+
await plugin.call('sidePanel', 'showContent', 'udapp' )
149150
plugin.call('udapp', 'addInstance', result.contractAddress, data.abi, args.contractName, data)
150151

151152
return this.createSuccessResult(result);

libs/remix-ai-core/src/types/mcp.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,51 @@ export interface IMCPAwareParams {
204204
/** MCP-specific parameters */
205205
mcp?: IEnhancedMCPProviderParams;
206206
}
207+
208+
/**
209+
* Record of a single tool call during code execution
210+
*/
211+
export interface IToolCallRecord {
212+
/** Name of the tool that was called */
213+
name: string;
214+
/** Arguments passed to the tool */
215+
arguments: Record<string, any>;
216+
/** Full result payload from the tool */
217+
result: IMCPToolResult;
218+
/** Execution time for this specific tool call in milliseconds */
219+
executionTime: number;
220+
}
221+
222+
/**
223+
* Code execution result for MCP code mode
224+
*/
225+
export interface ICodeExecutionResult {
226+
/** Whether the execution was successful */
227+
success: boolean;
228+
/** Console output from the execution */
229+
output: string;
230+
/** Error message if execution failed */
231+
error?: string;
232+
/** Execution time in milliseconds */
233+
executionTime: number;
234+
/** List of MCP tools called during execution */
235+
toolsCalled: string[];
236+
/** Full records of all tool calls with their payloads */
237+
toolCallRecords: IToolCallRecord[];
238+
/** Return value from the executed code */
239+
returnValue?: any;
240+
}
241+
242+
/**
243+
* Execution context provided to code execution sandbox
244+
*/
245+
export interface IExecutionContext {
246+
/** Execute an MCP tool by name */
247+
executeToolCall: (name: string, args: Record<string, any>) => Promise<IMCPToolResult>;
248+
/** Console interface for logging */
249+
console: {
250+
log: (...args: any[]) => void;
251+
error: (...args: any[]) => void;
252+
warn: (...args: any[]) => void;
253+
};
254+
}

0 commit comments

Comments
 (0)