feat(mcp): add Model Context Protocol integration
Add MCP integration including: - New MCP settings tab with server configuration - Tool invocation UI components - API endpoints for MCP management - Integration with chat system for tool execution - Example configurations
This commit is contained in:
115
app/lib/stores/mcp.ts
Normal file
115
app/lib/stores/mcp.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { create } from 'zustand';
|
||||
import type { MCPConfig, MCPServerTools } from '~/lib/services/mcpService';
|
||||
|
||||
const MCP_SETTINGS_KEY = 'mcp_settings';
|
||||
const isBrowser = typeof window !== 'undefined';
|
||||
|
||||
type MCPSettings = {
|
||||
mcpConfig: MCPConfig;
|
||||
maxLLMSteps: number;
|
||||
};
|
||||
|
||||
const defaultSettings = {
|
||||
maxLLMSteps: 5,
|
||||
mcpConfig: {
|
||||
mcpServers: {},
|
||||
},
|
||||
} satisfies MCPSettings;
|
||||
|
||||
type Store = {
|
||||
isInitialized: boolean;
|
||||
settings: MCPSettings;
|
||||
serverTools: MCPServerTools;
|
||||
error: string | null;
|
||||
isUpdatingConfig: boolean;
|
||||
};
|
||||
|
||||
type Actions = {
|
||||
initialize: () => Promise<void>;
|
||||
updateSettings: (settings: MCPSettings) => Promise<void>;
|
||||
checkServersAvailabilities: () => Promise<void>;
|
||||
};
|
||||
|
||||
export const useMCPStore = create<Store & Actions>((set, get) => ({
|
||||
isInitialized: false,
|
||||
settings: defaultSettings,
|
||||
serverTools: {},
|
||||
error: null,
|
||||
isUpdatingConfig: false,
|
||||
initialize: async () => {
|
||||
if (get().isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isBrowser) {
|
||||
const savedConfig = localStorage.getItem(MCP_SETTINGS_KEY);
|
||||
|
||||
if (savedConfig) {
|
||||
try {
|
||||
const settings = JSON.parse(savedConfig) as MCPSettings;
|
||||
const serverTools = await updateServerConfig(settings.mcpConfig);
|
||||
set(() => ({ settings, serverTools }));
|
||||
} catch (error) {
|
||||
console.error('Error parsing saved mcp config:', error);
|
||||
set(() => ({
|
||||
error: `Error parsing saved mcp config: ${error instanceof Error ? error.message : String(error)}`,
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
localStorage.setItem(MCP_SETTINGS_KEY, JSON.stringify(defaultSettings));
|
||||
}
|
||||
}
|
||||
|
||||
set(() => ({ isInitialized: true }));
|
||||
},
|
||||
updateSettings: async (newSettings: MCPSettings) => {
|
||||
if (get().isUpdatingConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
set(() => ({ isUpdatingConfig: true }));
|
||||
|
||||
const serverTools = await updateServerConfig(newSettings.mcpConfig);
|
||||
|
||||
if (isBrowser) {
|
||||
localStorage.setItem(MCP_SETTINGS_KEY, JSON.stringify(newSettings));
|
||||
}
|
||||
|
||||
set(() => ({ settings: newSettings, serverTools }));
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
set(() => ({ isUpdatingConfig: false }));
|
||||
}
|
||||
},
|
||||
checkServersAvailabilities: async () => {
|
||||
const response = await fetch('/api/mcp-check', {
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Server responded with ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const serverTools = (await response.json()) as MCPServerTools;
|
||||
|
||||
set(() => ({ serverTools }));
|
||||
},
|
||||
}));
|
||||
|
||||
async function updateServerConfig(config: MCPConfig) {
|
||||
const response = await fetch('/api/mcp-update-config', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(config),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Server responded with ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = (await response.json()) as MCPServerTools;
|
||||
|
||||
return data;
|
||||
}
|
||||
Reference in New Issue
Block a user