Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
c3ab792
feat: add n8n import UI
abhijeet-akto Dec 5, 2025
f3350c4
feat: add API for N8N import
abhijeet-akto Dec 5, 2025
4718440
chore: add database integration
abhijeet-akto Dec 5, 2025
0ee9315
chore: change collection name to generic
abhijeet-akto Dec 5, 2025
aca1368
fix: config structure
abhijeet-akto Dec 5, 2025
3a9f9a1
feat: add cron to schedule jobs
abhijeet-akto Dec 5, 2025
471d8e5
fix: issues with cron scheduler
abhijeet-akto Dec 6, 2025
d1913b9
feat: add langchain support
abhijeet-akto Dec 7, 2025
d1ab9b6
feat: add copilot studio support
abhijeet-akto Dec 7, 2025
9a7695f
chore: remove log statements
abhijeet-akto Dec 8, 2025
2b7a174
chore: add langchain logo
abhijeet-akto Dec 8, 2025
a9b2de6
feat: add cron support for AI agent
abhijeet-akto Dec 8, 2025
a02217e
feat: new implementation for scheduling ai agents jobs
abhijeet-akto Dec 9, 2025
ca1213b
feat: add azure download functionality
abhijeet-akto Dec 9, 2025
22861a8
chore: change job intervals
abhijeet-akto Dec 9, 2025
23cc09e
feat: add implementation for copilot and langchain
abhijeet-akto Dec 9, 2025
f92d6b5
fix: security valunerability
abhijeet-akto Dec 9, 2025
93008d0
fix: security valunerability
abhijeet-akto Dec 9, 2025
1c64776
fix: security valunerability
abhijeet-akto Dec 9, 2025
331e349
fix: security valunerability
abhijeet-akto Dec 9, 2025
9b8337c
fix: security valunerability
abhijeet-akto Dec 9, 2025
d785e77
fix: security valunerability
abhijeet-akto Dec 9, 2025
aad391c
fix: security valunerability
abhijeet-akto Dec 9, 2025
b5e5e69
Merge pull request #3720 from akto-api-security/abhi/feat/ai-agent-cr…
abhijeet-akto Dec 9, 2025
b682f0c
fix: security valunerability
abhijeet-akto Dec 9, 2025
dd68849
fix: security valunerability
abhijeet-akto Dec 9, 2025
a312c47
fix: security valunerability
abhijeet-akto Dec 9, 2025
bfe84eb
fix: security valunerability
abhijeet-akto Dec 9, 2025
722fe0e
fix: security valunerability
abhijeet-akto Dec 9, 2025
458dd0f
fix: security valunerability
abhijeet-akto Dec 9, 2025
548c056
fix: security valunerability
abhijeet-akto Dec 9, 2025
da4e570
fix: security valunerability
abhijeet-akto Dec 9, 2025
fd5c566
fix: security valunerability
abhijeet-akto Dec 9, 2025
f699ed2
fix: security valunerability
abhijeet-akto Dec 9, 2025
34caefe
fix: security valunerability
abhijeet-akto Dec 9, 2025
92cacc0
fix: security valunerability
abhijeet-akto Dec 9, 2025
1716090
fix: security valunerability
abhijeet-akto Dec 9, 2025
1a2b100
fix: security valunerability
abhijeet-akto Dec 9, 2025
b472977
Update libs/utils/src/main/java/com/akto/jobs/executors/AIAgentConnec…
abhijeet-akto Dec 9, 2025
2c85780
Update libs/utils/src/main/java/com/akto/jobs/executors/AIAgentConnec…
abhijeet-akto Dec 9, 2025
dd7ab2f
Update libs/utils/src/main/java/com/akto/jobs/executors/AIAgentConnec…
abhijeet-akto Dec 9, 2025
a494869
Update libs/utils/src/main/java/com/akto/jobs/executors/AIAgentConnec…
abhijeet-akto Dec 9, 2025
dce6fd1
Update libs/utils/src/main/java/com/akto/jobs/executors/AIAgentConnec…
abhijeet-akto Dec 9, 2025
a8879bf
Update libs/utils/src/main/java/com/akto/jobs/executors/AIAgentConnec…
abhijeet-akto Dec 9, 2025
246a0c6
Update libs/utils/src/main/java/com/akto/jobs/executors/AIAgentConnec…
abhijeet-akto Dec 9, 2025
a7b04f6
Update libs/utils/src/main/java/com/akto/jobs/executors/AIAgentConnec…
abhijeet-akto Dec 9, 2025
2980494
Update libs/utils/src/main/java/com/akto/jobs/executors/AIAgentConnec…
abhijeet-akto Dec 9, 2025
7a3ea6a
Update libs/utils/src/main/java/com/akto/jobs/executors/AIAgentConnec…
abhijeet-akto Dec 9, 2025
3c71b16
Update libs/utils/src/main/java/com/akto/jobs/executors/AIAgentConnec…
abhijeet-akto Dec 9, 2025
ea9214a
fix: security valunerability
abhijeet-akto Dec 9, 2025
f5dab11
fix: security valunerability
abhijeet-akto Dec 9, 2025
7efb3b4
fix: security valunerability
abhijeet-akto Dec 9, 2025
eae8e77
Merge pull request #3729 from akto-api-security/abhi/feat/ai-agent-cr…
abhijeet-akto Dec 9, 2025
d1c7d4e
chore: resolve pr comments
abhijeet-akto Dec 9, 2025
d44e07c
chore: code cleaning
abhijeet-akto Dec 9, 2025
43c703f
chore: remove unused code
abhijeet-akto Dec 9, 2025
58cd439
chore: resolved pr comments
abhijeet-akto Dec 10, 2025
152733b
chore: resolved pr comments
abhijeet-akto Dec 10, 2025
197daac
feat: add auth support for api key
abhijeet-akto Dec 10, 2025
9737cd7
feat: add auth logic
abhijeet-akto Dec 10, 2025
47adc21
chore: remove auth logic
abhijeet-akto Dec 10, 2025
d3dd787
chore: remove auth logic
abhijeet-akto Dec 10, 2025
f0c164e
feat: add jwt auth logic
abhijeet-akto Dec 10, 2025
8742fbf
chore: updated documentation
abhijeet-akto Dec 10, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package com.akto.action;

import com.akto.dao.context.Context;
import com.akto.dto.jobs.AIAgentConnectorSyncJobParams;
import com.akto.dto.jobs.Job;
import com.akto.dto.jobs.JobExecutorType;
import com.akto.jobs.JobScheduler;
import com.akto.log.LoggerMaker;
import com.akto.log.LoggerMaker.LogDb;
import com.opensymphony.xwork2.Action;
import lombok.Getter;
import lombok.Setter;

import java.util.HashMap;
import java.util.Map;

import static com.akto.jobs.executors.AIAgentConnectorConstants.*;
import static com.akto.jobs.executors.AIAgentConnectorUtils.*;

/**
* Unified action for importing AI Agent Connector data (N8N, Langchain, Copilot Studio).
* This action schedules recurring jobs to sync data from various AI agent platforms.
*/
@Getter
@Setter
public class AIAgentConnectorImportAction extends UserAction {

private static final LoggerMaker loggerMaker = new LoggerMaker(AIAgentConnectorImportAction.class, LogDb.DASHBOARD);

// Action parameters
private String connectorType;
private String dataIngestionUrl;
private String jobId;
private Integer recurringIntervalSeconds;

// N8N-specific parameters
private String n8nUrl;
private String n8nApiKey;

// Langchain-specific parameters
private String langsmithUrl;
private String langsmithApiKey;

// Copilot Studio-specific parameters
private String appInsightsAppId;
private String appInsightsApiKey;

/**
* Unified method to initiate import for any AI Agent Connector.
* The connector type is determined by the connectorType parameter.
*/
public String initiateImport() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a get API as well ?? Where are we going to show the connected agents and their api keys etc ..?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not currently on the UI.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a release blocker but we should show that somewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

try {
loggerMaker.info("Initiating import for connector type: " + connectorType, LogDb.DASHBOARD);

// Validate connector type
if (!isValidConnectorType(connectorType)) {
loggerMaker.error("Invalid connector type: " + connectorType, LogDb.DASHBOARD);
return Action.ERROR.toUpperCase();
}

// Build configuration based on connector type
Map<String, String> config = buildConfig();
if (config == null) {
return Action.ERROR.toUpperCase();
}

// Determine recurring interval (use provided value or default)
int interval = (recurringIntervalSeconds != null && recurringIntervalSeconds > 0)
? recurringIntervalSeconds
: DEFAULT_RECURRING_INTERVAL_SECONDS;

// Schedule recurring job using JobScheduler
Job job = JobScheduler.scheduleRecurringJob(
Context.accountId.get(),
new AIAgentConnectorSyncJobParams(
connectorType,
config,
Context.now()
),
JobExecutorType.DASHBOARD,
interval
);

if (job == null) {
loggerMaker.error("Failed to schedule recurring job for " + connectorType + " connector", LogDb.DASHBOARD);
return Action.ERROR.toUpperCase();
}

this.jobId = job.getId().toHexString();
loggerMaker.info("Successfully scheduled recurring job for " + connectorType + " connector with job ID: " + this.jobId + ", interval: " + interval + "s", LogDb.DASHBOARD);

return Action.SUCCESS.toUpperCase();

} catch (Exception e) {
loggerMaker.error("Error initiating " + connectorType + " import: " + e.getMessage(), LogDb.DASHBOARD);
return Action.ERROR.toUpperCase();
}
}

/**
* Builds configuration map based on connector type.
*/
private Map<String, String> buildConfig() {
Map<String, String> config = new HashMap<>();
config.put(CONFIG_DATA_INGESTION_SERVICE_URL, dataIngestionUrl);

switch (connectorType) {
case CONNECTOR_TYPE_N8N:
if (n8nUrl == null || n8nUrl.isEmpty() || n8nApiKey == null || n8nApiKey.isEmpty()) {
loggerMaker.error("Missing required N8N configuration", LogDb.DASHBOARD);
return null;
}
config.put(CONFIG_N8N_BASE_URL, n8nUrl);
config.put(CONFIG_N8N_API_KEY, n8nApiKey);
break;

case CONNECTOR_TYPE_LANGCHAIN:
if (langsmithUrl == null || langsmithUrl.isEmpty() || langsmithApiKey == null || langsmithApiKey.isEmpty()) {
loggerMaker.error("Missing required Langchain configuration", LogDb.DASHBOARD);
return null;
}
config.put(CONFIG_LANGSMITH_BASE_URL, langsmithUrl);
config.put(CONFIG_LANGSMITH_API_KEY, langsmithApiKey);
break;

case CONNECTOR_TYPE_COPILOT_STUDIO:
if (appInsightsAppId == null || appInsightsAppId.isEmpty() || appInsightsApiKey == null || appInsightsApiKey.isEmpty()) {
loggerMaker.error("Missing required Copilot Studio configuration", LogDb.DASHBOARD);
return null;
}
config.put(CONFIG_APPINSIGHTS_APP_ID, appInsightsAppId);
config.put(CONFIG_APPINSIGHTS_API_KEY, appInsightsApiKey);
break;

default:
loggerMaker.error("Unsupported connector type: " + connectorType, LogDb.DASHBOARD);
return null;
}

return config;
}
}
28 changes: 28 additions & 0 deletions apps/dashboard/src/main/resources/struts.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3143,6 +3143,34 @@
</result>
</action>

<!-- Unified AI Agent Connector Import Action -->
<action name="api/initiateAIAgentConnectorImport" class="com.akto.action.AIAgentConnectorImportAction" method="initiateImport">
<interceptor-ref name="json"/>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="roleAccessInterceptor">
<param name="featureLabel">INTEGRATIONS</param>
<param name="accessType">READ_WRITE</param>
<param name="actionDescription">User started AI Agent Connector Import</param>
</interceptor-ref>
<result name="FORBIDDEN" type="json">
<param name="statusCode">403</param>
<param name="ignoreHierarchy">false</param>
<param name="includeProperties">^actionErrors.*</param>
</result>
<result name="SUCCESS" type="json">
</result>
<result name="ERROR" type="json">
<param name="statusCode">422</param>
<param name="ignoreHierarchy">false</param>
<param name="includeProperties">^actionErrors.*</param>
</result>
<result name="UNAUTHORIZED" type="json">
<param name="statusCode">403</param>
<param name="ignoreHierarchy">false</param>
<param name="includeProperties">^actionErrors.*</param>
</result>
</action>

<action name="api/initiateMCPRecon" class="com.akto.action.MCPReconAction" method="initiateMCPRecon">
<interceptor-ref name="json"/>
<interceptor-ref name="defaultStack" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,19 @@ const api = {
})
},

initiateAIAgentConnectorImport(connectorType, connectorConfig, dataIngestionUrl, recurringIntervalSeconds) {
return request({
url: '/api/initiateAIAgentConnectorImport',
method: 'post',
data: {
connectorType,
dataIngestionUrl,
recurringIntervalSeconds,
...connectorConfig
}
})
},

}

export default api
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { Box, Button, ButtonGroup, Divider, Text, TextField, VerticalStack } from '@shopify/polaris';
import React, { useState } from 'react';
import api from '../api';
import func from "@/util/func";
import PasswordTextField from '../../../components/layouts/PasswordTextField';

/**
* Common component for AI Agent Connector imports (N8N, Langchain, Copilot Studio)
*/
const AIAgentConnectorImport = ({
connectorType,
connectorName,
description,
fields,
docsUrl,
recurringIntervalSeconds = 300,
onSuccess
}) => {
const [loading, setLoading] = useState(false);
const [formData, setFormData] = useState(
fields.reduce((acc, field) => ({ ...acc, [field.name]: '' }), {})
);

const goToDocs = () => {
window.open(docsUrl);
};

const validateForm = () => {
for (const field of fields) {
const value = formData[field.name];
if (!value || value.length === 0) {
func.setToast(true, true, `Please enter a valid ${field.label}.`);
return false;
}
}
return true;
};

const buildConnectorConfig = () => {
const config = {};
fields.forEach(field => {
if (field.configKey) {
config[field.configKey] = formData[field.name];
}
});
return config;
};

const primaryAction = () => {
if (!validateForm()) {
return;
}

setLoading(true);
const connectorConfig = buildConnectorConfig();
const dataIngestionUrl = formData.dataIngestionUrl;

api.initiateAIAgentConnectorImport(
connectorType,
connectorConfig,
dataIngestionUrl,
recurringIntervalSeconds
).then((res) => {
func.setToast(true, false, `${connectorName} Import initiated successfully. Please check your dashboard for updates.`);
if (onSuccess) {
onSuccess(res);
}
}).catch((err) => {
console.error(`Error initiating ${connectorName} import:`, err);
func.setToast(true, true, `Ensure that you have added the correct ${connectorName} credentials.`);
}).finally(() => {
setLoading(false);
// Reset form
setFormData(fields.reduce((acc, field) => ({ ...acc, [field.name]: '' }), {}));
});
};

const isFormValid = () => {
return fields.every(field => formData[field.name]?.length > 0);
};

const updateField = (fieldName, value) => {
setFormData(prev => ({ ...prev, [fieldName]: value }));
};

return (
<div className='card-items'>
<Text variant='bodyMd'>
{description}
</Text>

<Box paddingBlockStart={3}><Divider /></Box>

<VerticalStack gap="2">
{fields.map((field) => {
if (field.type === 'password') {
return (
<PasswordTextField
key={field.name}
label={field.label}
setField={(value) => updateField(field.name, value)}
onFunc={true}
field={formData[field.name]}
placeholder={field.placeholder}
/>
);
}

return (
<TextField
key={field.name}
label={field.label}
value={formData[field.name]}
type={field.type || 'text'}
onChange={(value) => updateField(field.name, value)}
placeholder={field.placeholder}
/>
);
})}

<ButtonGroup>
<Button
onClick={primaryAction}
primary
disabled={!isFormValid()}
loading={loading}
>
Import
</Button>
<Button onClick={goToDocs}>Go to docs</Button>
</ButtonGroup>
</VerticalStack>
</div>
);
};

export default AIAgentConnectorImport;
Loading
Loading