Skip to content

Commit 1669e3a

Browse files
authored
Merge pull request #6608 from remix-project-org/test_amp
add mcp amp
2 parents 905b91e + 803dfa0 commit 1669e3a

File tree

10 files changed

+1261
-14
lines changed

10 files changed

+1261
-14
lines changed

apps/remix-ide/src/app/plugins/config.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const profile = {
66
name: 'config',
77
displayName: 'Config',
88
description: 'Config',
9-
methods: ['getAppParameter', 'setAppParameter'],
9+
methods: ['getAppParameter', 'setAppParameter', 'getEnv'],
1010
events: ['configChanged']
1111
}
1212

@@ -29,4 +29,16 @@ export class ConfigPlugin extends Plugin {
2929
const config = Registry.getInstance().get('config').api
3030
config.set(name, value)
3131
}
32+
33+
async getEnv (key: string): Promise<string | undefined> {
34+
const env: string = await this.call('fileManager', 'readFile', '.env')
35+
let value
36+
env.split('\n').forEach((line: string) => {
37+
const [envKey, envValue] = line.split('=');
38+
if (envKey === key) {
39+
value = envValue;
40+
}
41+
})
42+
return value
43+
}
3244
}

libs/remix-ai-core/src/remix-mcp-server/RemixMCPServer.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@ import { createDeploymentTools } from './handlers/DeploymentHandler';
3232
import { createDebuggingTools } from './handlers/DebuggingHandler';
3333
import { createCodeAnalysisTools } from './handlers/CodeAnalysisHandler';
3434
import { createTutorialsTools } from './handlers/TutorialsHandler';
35+
import { createAmpTools } from './handlers/AmpHandler';
3536

3637
// Import resource providers
3738
import { ProjectResourceProvider } from './providers/ProjectResourceProvider';
3839
import { CompilationResourceProvider } from './providers/CompilationResourceProvider';
3940
import { DeploymentResourceProvider } from './providers/DeploymentResourceProvider';
4041
import { TutorialsResourceProvider } from './providers/TutorialsResourceProvider';
42+
import { AmpResourceProvider } from './providers/AmpResourceProvider';
4143

4244
/**
4345
* Main Remix MCP Server implementation
@@ -454,10 +456,14 @@ export class RemixMCPServer extends EventEmitter implements IRemixMCPServer {
454456
const codeAnalysisTools = createCodeAnalysisTools();
455457
this._tools.registerBatch(codeAnalysisTools);
456458

457-
// Register debugging tools
459+
// Register tutorial tools
458460
const tutorialTools = createTutorialsTools();
459461
this._tools.registerBatch(tutorialTools);
460462

463+
// Register Amp tools
464+
const ampTools = createAmpTools();
465+
this._tools.registerBatch(ampTools);
466+
461467
const totalTools = this._tools.list().length;
462468

463469
} catch (error) {
@@ -484,10 +490,14 @@ export class RemixMCPServer extends EventEmitter implements IRemixMCPServer {
484490
const deploymentProvider = new DeploymentResourceProvider();
485491
this._resources.register(deploymentProvider);
486492

487-
// Register turorial resource provider
493+
// Register tutorial resource provider
488494
const tutorialsProvider = new TutorialsResourceProvider(this._plugin);
489495
this._resources.register(tutorialsProvider);
490496

497+
// Register Amp resource provider
498+
const ampProvider = new AmpResourceProvider(this._plugin);
499+
this._resources.register(ampProvider);
500+
491501
const totalProviders = this._resources.list().length;
492502

493503
} catch (error) {
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
/* eslint-disable no-async-promise-executor */
2+
/**
3+
* Amp Query Tool Handlers for Remix MCP Server
4+
*
5+
* Provides functionality to query data using the Amp hosted server
6+
*/
7+
8+
import { IMCPToolResult } from '../../types/mcp';
9+
import { BaseToolHandler } from '../registry/RemixToolRegistry';
10+
import {
11+
ToolCategory,
12+
RemixToolDefinition
13+
} from '../types/mcpTools';
14+
import { Plugin } from '@remixproject/engine';
15+
16+
/**
17+
* Amp Query argument types
18+
*/
19+
export interface AmpQueryArgs {
20+
query: string
21+
}
22+
23+
/**
24+
* Amp Query result types
25+
*/
26+
export interface AmpQueryResult<T = any> {
27+
success: boolean;
28+
data: Array<T>;
29+
rowCount: number;
30+
query: string;
31+
error?: string;
32+
}
33+
34+
/**
35+
* Create an Amp client with the given configuration
36+
*/
37+
async function createAmpClient(baseUrl?: string, authToken?: string) {
38+
// Dynamic import for ES module packages
39+
// @ts-ignore - ES module dynamic import
40+
const { createConnectTransport } = await import("@connectrpc/connect-web");
41+
// @ts-ignore - ES module dynamic import
42+
const { createAuthInterceptor, createClient } = await import("@edgeandnode/amp");
43+
44+
const ampBaseUrl = baseUrl || "/amp";
45+
46+
const transport = createConnectTransport({
47+
baseUrl: ampBaseUrl,
48+
/**
49+
* If present, adds the auth token to the interceptor path.
50+
* This adds it to the connect-rpc transport layer and is passed to requests.
51+
* This is REQUIRED for querying published datasets through the gateway
52+
*/
53+
interceptors: authToken
54+
? [createAuthInterceptor(authToken)]
55+
: undefined,
56+
});
57+
58+
return createClient(transport);
59+
}
60+
61+
/**
62+
* Performs the given query with the AmpClient instance.
63+
* Waits for all batches to complete/resolve before returning.
64+
* @param query the query to run
65+
* @param baseUrl optional base URL for the Amp server
66+
* @param authToken optional authentication token
67+
* @returns an array of the results from all resolved batches
68+
*/
69+
async function performAmpQuery<T = any>(
70+
query: string,
71+
baseUrl?: string,
72+
authToken?: string
73+
): Promise<Array<T>> {
74+
const ampClient = await createAmpClient(baseUrl, authToken)
75+
const data: Array<T> = []
76+
77+
for await (const batch of ampClient.query(query)) {
78+
data.push(...batch)
79+
}
80+
81+
return data
82+
}
83+
84+
/**
85+
* Amp Query Tool Handler
86+
*/
87+
export class AmpQueryHandler extends BaseToolHandler {
88+
name = 'amp_query';
89+
description = 'Execute SQL queries against the Amp hosted server to retrieve blockchain data';
90+
inputSchema = {
91+
type: 'object',
92+
properties: {
93+
query: {
94+
type: 'string',
95+
description: 'SQL query to execute against the Amp server'
96+
}
97+
},
98+
required: ['query']
99+
};
100+
101+
getPermissions(): string[] {
102+
return ['amp:query'];
103+
}
104+
105+
validate(args: AmpQueryArgs): boolean | string {
106+
const required = this.validateRequired(args, ['query']);
107+
if (required !== true) return required;
108+
109+
const types = this.validateTypes(args, {
110+
query: 'string'
111+
});
112+
if (types !== true) return types;
113+
114+
if (args.query.trim().length === 0) {
115+
return 'Query cannot be empty';
116+
}
117+
118+
return true;
119+
}
120+
121+
async execute(args: AmpQueryArgs, plugin: Plugin): Promise<IMCPToolResult> {
122+
try {
123+
// Show a notification that the query is being executed
124+
plugin.call('notification', 'toast', `Executing Amp query...`);
125+
126+
const authToken: string | undefined = await plugin.call('config', 'getEnv', 'AMP_QUERY_TOKEN');
127+
const baseUrl: string | undefined = await plugin.call('config', 'getEnv', 'AMP_QUERY_URL');
128+
// Perform the Amp query
129+
const data = await performAmpQuery(
130+
args.query,
131+
baseUrl,
132+
authToken
133+
);
134+
135+
const result: AmpQueryResult = {
136+
success: true,
137+
data: data,
138+
rowCount: data.length,
139+
query: args.query
140+
};
141+
142+
// Show success notification
143+
plugin.call('notification', 'toast', `Query completed successfully. Retrieved ${data.length} rows.`);
144+
145+
return this.createSuccessResult(result);
146+
147+
} catch (error) {
148+
console.error('Amp query error:', error);
149+
150+
const errorMessage = error instanceof Error ? error.message : String(error);
151+
152+
// Show error notification
153+
plugin.call('notification', 'toast', `Amp query failed: ${errorMessage}`);
154+
155+
return this.createErrorResult(`Amp query failed: ${errorMessage}`);
156+
}
157+
}
158+
}
159+
160+
/**
161+
* Amp Dataset Manifest argument types
162+
*/
163+
export interface AmpDatasetManifestArgs {
164+
datasetName: string;
165+
version: string;
166+
}
167+
168+
/**
169+
* Amp Dataset Manifest result types
170+
*/
171+
export interface AmpDatasetManifestResult {
172+
success: boolean;
173+
manifest?: any;
174+
datasetName: string;
175+
version: string;
176+
error?: string;
177+
}
178+
179+
/**
180+
* Amp Dataset Manifest Tool Handler
181+
*/
182+
export class AmpDatasetManifestHandler extends BaseToolHandler {
183+
name = 'amp_dataset_manifest';
184+
description = 'Fetch manifest information for a specific Amp dataset version';
185+
inputSchema = {
186+
type: 'object',
187+
properties: {
188+
datasetName: {
189+
type: 'string',
190+
description: 'Dataset name in format owner/name (e.g., "shiyasmohd/counter")'
191+
},
192+
version: {
193+
type: 'string',
194+
description: 'Dataset version (e.g., "0.0.2")'
195+
}
196+
},
197+
required: ['datasetName', 'version']
198+
};
199+
200+
getPermissions(): string[] {
201+
return ['amp:dataset:manifest'];
202+
}
203+
204+
validate(args: AmpDatasetManifestArgs): boolean | string {
205+
const required = this.validateRequired(args, ['datasetName', 'version']);
206+
if (required !== true) return required;
207+
208+
const types = this.validateTypes(args, {
209+
datasetName: 'string',
210+
version: 'string'
211+
});
212+
if (types !== true) return types;
213+
214+
if (args.datasetName.trim().length === 0) {
215+
return 'Dataset name cannot be empty';
216+
}
217+
218+
if (args.version.trim().length === 0) {
219+
return 'Version cannot be empty';
220+
}
221+
222+
return true;
223+
}
224+
225+
async execute(args: AmpDatasetManifestArgs, plugin: Plugin): Promise<IMCPToolResult> {
226+
try {
227+
// Show a notification that the manifest is being fetched
228+
plugin.call('notification', 'toast', `Fetching manifest for ${args.datasetName}@${args.version}...`);
229+
230+
const url = `https://api.registry.amp.staging.thegraph.com/api/v1/datasets/${args.datasetName}/versions/${args.version}/manifest`;
231+
232+
const response = await fetch(url);
233+
234+
if (!response.ok) {
235+
throw new Error(`HTTP error! status: ${response.status}`);
236+
}
237+
238+
const manifest = await response.json();
239+
240+
const result: AmpDatasetManifestResult = {
241+
success: true,
242+
manifest: manifest,
243+
datasetName: args.datasetName,
244+
version: args.version
245+
};
246+
247+
// Show success notification
248+
plugin.call('notification', 'toast', `Manifest fetched successfully for ${args.datasetName}@${args.version}`);
249+
250+
return this.createSuccessResult(result);
251+
252+
} catch (error) {
253+
console.error('Amp dataset manifest fetch error:', error);
254+
255+
const errorMessage = error instanceof Error ? error.message : String(error);
256+
257+
// Show error notification
258+
plugin.call('notification', 'toast', `Failed to fetch manifest: ${errorMessage}`);
259+
260+
return this.createErrorResult(`Failed to fetch manifest: ${errorMessage}`);
261+
}
262+
}
263+
}
264+
265+
/**
266+
* Create Amp tool definitions
267+
*/
268+
export function createAmpTools(): RemixToolDefinition[] {
269+
return [
270+
{
271+
name: 'amp_query',
272+
description: 'Execute SQL queries against the Amp hosted server to retrieve blockchain data',
273+
inputSchema: new AmpQueryHandler().inputSchema,
274+
category: ToolCategory.ANALYSIS,
275+
permissions: ['amp:query'],
276+
handler: new AmpQueryHandler()
277+
},
278+
{
279+
name: 'amp_dataset_manifest',
280+
description: 'Fetch manifest information for a specific Amp dataset version',
281+
inputSchema: new AmpDatasetManifestHandler().inputSchema,
282+
category: ToolCategory.ANALYSIS,
283+
permissions: ['amp:dataset:manifest'],
284+
handler: new AmpDatasetManifestHandler()
285+
}
286+
];
287+
}

libs/remix-ai-core/src/remix-mcp-server/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ export { createCompilationTools } from './handlers/CompilationHandler';
1717
export { createDeploymentTools } from './handlers/DeploymentHandler';
1818
export { createDebuggingTools } from './handlers/DebuggingHandler';
1919
export { createCodeAnalysisTools } from './handlers/CodeAnalysisHandler';
20+
export { createAmpTools } from './handlers/AmpHandler';
2021

2122
// Resource Providers
2223
export { ProjectResourceProvider } from './providers/ProjectResourceProvider';
2324
export { CompilationResourceProvider } from './providers/CompilationResourceProvider';
2425
export { DeploymentResourceProvider } from './providers/DeploymentResourceProvider';
2526
export { TutorialsResourceProvider } from './providers/TutorialsResourceProvider';
27+
export { AmpResourceProvider } from './providers/AmpResourceProvider';
2628

2729
// Middleware
2830
export {

0 commit comments

Comments
 (0)