feat: MCP Server plugin (Task 5.16) - expose LeoCRM tools to external AI clients
- New plugin app/plugins/builtins/mcp_server/ with 9 MCP tools
- Tools: search_contacts, get_contact, create_contact, list_calendar_entries,
create_calendar_entry, list_emails, send_email, list_files, upload_file
- Routes: GET /api/v1/mcp/tools, POST /api/v1/mcp/tools/{name}/execute, GET /api/v1/mcp/config
- API-token auth via session + RBAC (mcp:read, mcp:write)
- Frontend: mcp.ts API client with React Query hooks
- Frontend: SettingsMcp.tsx settings page with tool listing and execution
- i18n: de.json and en.json updated with MCP entries
- Tests: 7 tests covering tool listing, config, execution, auth, schema validation
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* MCP Server plugin API client.
|
||||
*
|
||||
* Exposes LeoCRM tools to external MCP clients (Claude Desktop, etc.).
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { apiGet, apiPost } from './client';
|
||||
|
||||
// ─── Types ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface McpToolParameter {
|
||||
name: string;
|
||||
type: string;
|
||||
description: string;
|
||||
required: boolean;
|
||||
default?: unknown;
|
||||
}
|
||||
|
||||
export interface McpToolDefinition {
|
||||
name: string;
|
||||
description: string;
|
||||
category: string;
|
||||
parameters: McpToolParameter[];
|
||||
required_permission: string | null;
|
||||
}
|
||||
|
||||
export interface McpToolListResponse {
|
||||
tools: McpToolDefinition[];
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface McpToolExecuteRequest {
|
||||
arguments: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface McpToolExecuteResponse {
|
||||
tool: string;
|
||||
success: boolean;
|
||||
result: unknown;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export interface McpServerConfig {
|
||||
server_name: string;
|
||||
server_version: string;
|
||||
protocol_version: string;
|
||||
base_url: string;
|
||||
auth_method: string;
|
||||
available_tools: string[];
|
||||
}
|
||||
|
||||
// ─── API Functions ─────────────────────────────────────────────────────────
|
||||
|
||||
export function fetchMcpTools(): Promise<McpToolListResponse> {
|
||||
return apiGet<McpToolListResponse>('/mcp/tools');
|
||||
}
|
||||
|
||||
export function executeMcpTool(
|
||||
toolName: string,
|
||||
args: Record<string, unknown>
|
||||
): Promise<McpToolExecuteResponse> {
|
||||
return apiPost<McpToolExecuteResponse>(`/mcp/tools/${toolName}/execute`, {
|
||||
arguments: args,
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchMcpConfig(): Promise<McpServerConfig> {
|
||||
return apiGet<McpServerConfig>('/mcp/config');
|
||||
}
|
||||
|
||||
// ─── Hooks ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export function useMcpTools() {
|
||||
return useQuery({
|
||||
queryKey: ['mcp', 'tools'],
|
||||
queryFn: fetchMcpTools,
|
||||
staleTime: 60 * 1000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useMcpConfig() {
|
||||
return useQuery({
|
||||
queryKey: ['mcp', 'config'],
|
||||
queryFn: fetchMcpConfig,
|
||||
staleTime: 60 * 1000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useExecuteMcpTool() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
toolName,
|
||||
args,
|
||||
}: {
|
||||
toolName: string;
|
||||
args: Record<string, unknown>;
|
||||
}) => executeMcpTool(toolName, args),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['mcp'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user