UNPKG

@chinchillaenterprises/mcp-amplify

Version:

AWS Amplify MCP server with intelligent deployment automation, specialized logging suite, and recursive resource discovery

176 lines 8.01 kB
import * as fs from 'fs'; import path from 'path'; import { AppSyncClient, ListGraphqlApisCommand, ListDataSourcesCommand } from '@aws-sdk/client-appsync'; /** * Discover all resources in an Amplify sandbox * * Strategy: * 1. Read amplify_outputs.json to get GraphQL URL and auth config * 2. Query AppSync to find API by matching GraphQL URL * 3. List all data sources (DynamoDB tables + Lambda functions) * 4. Return complete sandbox resource inventory */ export async function handleAmplifyDiscoverSandboxResources(args) { try { // Get outputs file path const outputsPath = args?.outputsPath || path.join(process.cwd(), 'amplify_outputs.json'); // Read amplify_outputs.json if (!fs.existsSync(outputsPath)) { throw new Error(`amplify_outputs.json not found at ${outputsPath}. ` + `Make sure you've run "npx ampx sandbox" in your Amplify project.`); } const outputsContent = fs.readFileSync(outputsPath, 'utf-8'); const outputs = JSON.parse(outputsContent); // Validate outputs structure if (!outputs.data || !outputs.data.url) { throw new Error('Invalid amplify_outputs.json: missing data.url. ' + 'Make sure your sandbox has deployed successfully.'); } const targetGraphqlUrl = outputs.data.url; const region = outputs.data.aws_region || 'us-east-1'; // Initialize AppSync client const appSyncClient = new AppSyncClient({ region }); // Step 1: Find AppSync API by matching GraphQL URL let foundApi = null; let allApis = []; try { const listApisResponse = await appSyncClient.send(new ListGraphqlApisCommand({})); allApis = listApisResponse.graphqlApis || []; for (const api of allApis) { if (api.uris?.GRAPHQL === targetGraphqlUrl) { foundApi = api; break; } } } catch (error) { const errorMsg = error instanceof Error ? error.message : String(error); throw new Error(`Failed to query AppSync APIs: ${errorMsg}`); } if (!foundApi) { throw new Error(`Could not find AppSync API matching URL ${targetGraphqlUrl}. ` + `The sandbox may not be running. Run "npx ampx sandbox" to start it.`); } const apiId = foundApi.apiId; // Step 2: Get data sources for this API let dataSourcesResponse; try { dataSourcesResponse = await appSyncClient.send(new ListDataSourcesCommand({ apiId })); } catch (error) { const errorMsg = error instanceof Error ? error.message : String(error); throw new Error(`Failed to query AppSync data sources: ${errorMsg}`); } const dataSources = dataSourcesResponse.dataSources || []; // Step 3: Organize data sources by type const dynamodbTables = dataSources .filter(ds => ds.type === 'AMAZON_DYNAMODB') .map(ds => ({ name: ds.dynamodbConfig?.tableName, dataSourceName: ds.name, region: ds.dynamodbConfig?.awsRegion })); const lambdaFunctions = dataSources .filter(ds => ds.type === 'AWS_LAMBDA') .map(ds => { const arn = ds.lambdaConfig?.lambdaFunctionArn || ''; const functionName = arn.split(':').pop() || 'unknown'; return { name: functionName, arn: arn, dataSourceName: ds.name }; }); const otherDataSources = dataSources .filter(ds => ds.type !== 'AMAZON_DYNAMODB' && ds.type !== 'AWS_LAMBDA') .map(ds => ({ name: ds.name, type: ds.type })); // Step 4: Extract auth and model information const authConfig = outputs.auth ? { userPoolId: outputs.auth.user_pool_id, clientId: outputs.auth.user_pool_client_id, identityPoolId: outputs.auth.identity_pool_id, oauthProviders: outputs.auth.oauth?.identity_providers || [], oauthDomain: outputs.auth.oauth?.domain, passwordPolicy: outputs.auth.password_policy, mfaConfiguration: outputs.auth.mfa_configuration } : null; const models = outputs.data?.model_introspection?.models || {}; const modelNames = Object.keys(models); // Step 5: Build response return { success: true, sandbox: { apiId, graphqlUrl: targetGraphqlUrl, region, apiName: foundApi.name, defaultAuthType: foundApi.authenticationType, additionalAuthTypes: foundApi.additionalAuthenticationProviders?.map(auth => auth.authenticationType) || [] }, authentication: authConfig, dynamoDB: { count: dynamodbTables.length, tables: dynamodbTables }, lambda: { count: lambdaFunctions.length, functions: lambdaFunctions }, dataModels: { count: modelNames.length, models: modelNames.map(modelName => { const model = models[modelName]; const fields = Object.keys(model.fields || {}); const hasAuth = model.attributes?.some((a) => a.type === 'auth') || false; const indexes = model.attributes ?.filter((a) => a.type === 'key') .map((a) => a.properties?.name) || []; return { name: modelName, fieldCount: fields.length, fields: fields, hasAuthRules: hasAuth, customIndexes: indexes }; }) }, otherDataSources: { count: otherDataSources.length, sources: otherDataSources }, summary: { totalDataSources: dataSources.length, totalDynamoDBTables: dynamodbTables.length, totalLambdaFunctions: lambdaFunctions.length, totalDataModels: modelNames.length, authEnabled: authConfig !== null, hasOAuth: (authConfig?.oauthProviders?.length || 0) > 0 }, nextSteps: [ `Query DynamoDB tables: Use the table names above with AWS CLI or AWS SDK`, `Invoke Lambda functions: Use the function ARNs with AWS Lambda SDK`, `Query GraphQL API: Use ${targetGraphqlUrl} with your configured auth method`, `Use data models: Models are available via GraphQL queries: ${modelNames.slice(0, 3).join(', ')}${modelNames.length > 3 ? ', ...' : ''}` ] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); // Provide helpful context based on error type let helpText = ''; if (errorMessage.includes('not found') || errorMessage.includes('ENOENT')) { helpText = '\n\nMake sure you are in your Amplify project directory and have run: npx ampx sandbox'; } else if (errorMessage.includes('credentials') || errorMessage.includes('EAUTH')) { helpText = '\n\nMake sure your AWS credentials are configured: aws configure'; } else if (errorMessage.includes('permission')) { helpText = '\n\nYou may not have permission to access AppSync. Check your AWS IAM permissions.'; } throw new Error(`Failed to discover sandbox resources: ${errorMessage}${helpText}`); } } //# sourceMappingURL=sandbox-resource-discovery-handlers.js.map