rowboat/apps/rowboat/app/actions/custom-mcp-server.actions.ts
2025-08-23 08:15:58 +05:30

72 lines
2.5 KiB
TypeScript

'use server';
import { z } from 'zod';
import { CustomMcpServer } from "@/src/entities/models/project";
import { getMcpClient } from '../lib/mcp';
import { WorkflowTool } from '../lib/types/workflow_types';
import { authCheck } from './auth.actions';
import { container } from '@/di/container';
import { IAddCustomMcpServerController } from '@/src/interface-adapters/controllers/projects/add-custom-mcp-server.controller';
import { IRemoveCustomMcpServerController } from '@/src/interface-adapters/controllers/projects/remove-custom-mcp-server.controller';
type McpServerType = z.infer<typeof CustomMcpServer>;
function validateUrl(url: string): string {
try {
const parsedUrl = new URL(url);
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
throw new Error('Invalid protocol');
}
return parsedUrl.toString();
} catch (error) {
throw new Error('Invalid URL');
}
}
const addCustomMcpServerController = container.resolve<IAddCustomMcpServerController>('addCustomMcpServerController');
const removeCustomMcpServerController = container.resolve<IRemoveCustomMcpServerController>('removeCustomMcpServerController');
export async function addServer(projectId: string, name: string, server: McpServerType): Promise<void> {
const user = await authCheck();
// validate early for UX; use-case will validate again
validateUrl(server.serverUrl);
await addCustomMcpServerController.execute({
caller: 'user',
userId: user.id,
projectId,
name,
server,
});
}
export async function removeServer(projectId: string, name: string): Promise<void> {
const user = await authCheck();
await removeCustomMcpServerController.execute({
caller: 'user',
userId: user.id,
projectId,
name,
});
}
export async function fetchTools(serverUrl: string, serverName: string): Promise<z.infer<typeof WorkflowTool>[]> {
await authCheck();
const client = await getMcpClient(serverUrl, serverName);
const result = await client.listTools();
return result.tools.map(tool => {
return {
name: tool.name,
description: tool.description || '',
parameters: {
type: 'object',
properties: tool.inputSchema?.properties || {},
required: tool.inputSchema?.required || [],
additionalProperties: true,
},
isMcp: true,
mcpServerName: serverName,
mcpServerURL: serverUrl,
};
});
}