Coverage for /home/admin/Documents/AI/applications/lexigram-dev/lexigram/experimental/ai/lexigram-ai-mcp/src/lexigram/ai/mcp/config.py: 100%

97 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-25 07:19 +0800

1"""MCP server configuration for the Lexigram framework.""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import dataclass, field 

6from typing import ClassVar, cast 

7 

8from lexigram.ai.mcp.constants import ENV_NESTED_DELIMITER, ENV_PREFIX 

9from lexigram.config.base import BaseConfig 

10from lexigram.validation import ConfigDict, Field 

11 

12 

13@dataclass 

14class FilesystemConnectorConfig: 

15 """Configuration for the built-in FilesystemConnector.""" 

16 

17 root_dir: str = "" 

18 """Sandboxed root directory. Must be set to a non-empty path to enable.""" 

19 

20 read_only: bool = False 

21 """When True, write_file and other mutating tools are disabled.""" 

22 

23 

24@dataclass 

25class GitHubConnectorConfig: 

26 """Configuration for the built-in GitHubConnector.""" 

27 

28 token: str = "" 

29 """GitHub personal access token or fine-grained token.""" 

30 

31 api_url: str = "https://api.github.com" 

32 """Override for GitHub Enterprise installations.""" 

33 

34 

35@dataclass 

36class WebFetchConnectorConfig: 

37 """Configuration for the built-in WebFetchConnector.""" 

38 

39 enabled: bool = False 

40 """When True, the web-fetch tool is active (disabled by default).""" 

41 

42 max_content_bytes: int = 512 * 1024 

43 """Maximum response body size to fetch (default 512 KB).""" 

44 

45 user_agent: str = "lexigram-mcp/1.0" 

46 """HTTP User-Agent header to send.""" 

47 

48 

49@dataclass 

50class WebSearchConnectorConfig: 

51 """Configuration for the built-in WebSearchConnector.""" 

52 

53 provider: str = "brave" 

54 """Search provider name: 'brave', 'serpapi', or 'google'.""" 

55 

56 api_key: str = "" 

57 """API key for the configured search provider.""" 

58 

59 max_results: int = 10 

60 """Maximum number of results to return.""" 

61 

62 

63@dataclass 

64class SlackConnectorConfig: 

65 """Configuration for the built-in SlackConnector.""" 

66 

67 bot_token: str = "" 

68 """Slack bot OAuth token (xoxb-...).""" 

69 

70 max_messages: int = 100 

71 """Maximum messages to return per channel history request.""" 

72 

73 

74@dataclass 

75class GoogleDriveConnectorConfig: 

76 """Configuration for the built-in GoogleDriveConnector.""" 

77 

78 service_account_json: str = "" 

79 """Path to the Google service-account credentials JSON file.""" 

80 

81 impersonated_email: str = "" 

82 """Email to impersonate via domain-wide delegation (optional).""" 

83 

84 

85@dataclass 

86class SQLConnectorConfig: 

87 """Configuration for the built-in SQLConnector.""" 

88 

89 dsn: str = "" 

90 """Database connection string (e.g. postgresql://user:pass@host/db).""" 

91 

92 allowed_tables: list[str] = field(default_factory=list) 

93 """Explicit allowlist of table names that the connector may query.""" 

94 

95 read_only: bool = True 

96 """When True, only SELECT statements are permitted (default True).""" 

97 

98 

99@dataclass 

100class ConnectorsConfig: 

101 """Top-level connector configuration block inside ``MCPConfig``.""" 

102 

103 filesystem: FilesystemConnectorConfig = field( 

104 default_factory=FilesystemConnectorConfig 

105 ) 

106 github: GitHubConnectorConfig = field(default_factory=GitHubConnectorConfig) 

107 web_fetch: WebFetchConnectorConfig = field(default_factory=WebFetchConnectorConfig) 

108 web_search: WebSearchConnectorConfig = field( 

109 default_factory=WebSearchConnectorConfig 

110 ) 

111 slack: SlackConnectorConfig = field(default_factory=SlackConnectorConfig) 

112 google_drive: GoogleDriveConnectorConfig = field( 

113 default_factory=GoogleDriveConnectorConfig 

114 ) 

115 sql: SQLConnectorConfig = field(default_factory=SQLConnectorConfig) 

116 

117 

118@dataclass(init=False) 

119class MCPConfig(BaseConfig): 

120 """Configuration for the MCP server. 

121 

122 Attributes: 

123 host: Host to bind to (for HTTP transport). 

124 port: Port to bind to (for HTTP transport). 

125 path: URL path for MCP endpoint (default /mcp). 

126 enable_sse: Enable Server-Sent Events for streaming responses. 

127 stdio_mode: Use stdio transport instead of HTTP. 

128 server_name: Name of the MCP server. 

129 server_version: Version of the MCP server. 

130 cors_origins: CORS allowed origins (for HTTP transport). 

131 """ 

132 

133 config_section: ClassVar[str] = "ai_mcp" 

134 

135 model_config: ClassVar[ConfigDict] = cast( 

136 "ConfigDict", 

137 { 

138 "env_prefix": ENV_PREFIX, 

139 "env_nested_delimiter": ENV_NESTED_DELIMITER, 

140 "extra": "ignore", 

141 }, 

142 ) 

143 

144 enabled: bool = Field(default=True, description="Enable the MCP server subsystem") 

145 

146 host: str = Field(default="0.0.0.0") # noqa: S104 — config default, operator overridable 

147 """Host to bind to (for HTTP transport).""" 

148 

149 port: int = Field(default=8080, ge=1, le=65535) 

150 """Port to bind to (for HTTP transport).""" 

151 

152 path: str = Field(default="/mcp") 

153 """URL path for MCP endpoint.""" 

154 

155 enable_sse: bool = Field(default=True) 

156 """Enable Server-Sent Events for streaming responses.""" 

157 

158 stdio_mode: bool = Field(default=False) 

159 """Use stdio transport instead of HTTP.""" 

160 

161 server_name: str = Field(default="lexigram-mcp") 

162 """Name of the MCP server.""" 

163 

164 server_version: str = Field(default="1.0.0") 

165 """Version of the MCP server.""" 

166 

167 cors_origins: list[str] = field(default_factory=list) 

168 """CORS allowed origins (for HTTP transport).""" 

169 

170 max_request_size: int = Field(default=1024 * 1024, ge=1024) 

171 """Maximum request size in bytes.""" 

172 

173 request_timeout: float = Field(default=30.0, ge=1.0) 

174 """Request timeout in seconds.""" 

175 

176 allow_unauthenticated: bool = Field(default=False) 

177 """Permit non-authorizer request dispatch. 

178 

179 ``False`` (default) fails closed: after ``initialize``, requests for 

180 non-handshake methods are rejected with ``-32000`` unless an 

181 authorizer is bound or this flag is ``True``. ``True`` is an explicit 

182 opt-out restoring the open posture for local/development use. 

183 """ 

184 

185 # Client-side configuration 

186 client_url: str | None = Field(default=None) 

187 """URL of an external MCP server to connect to as a client. 

188 

189 When set, :class:`~lexigram.ai.mcp.client.MCPClient` is registered in the 

190 container using :class:`~lexigram.ai.mcp.client.SSEClientTransport`. 

191 For stdio-based clients, construct :class:`~lexigram.ai.mcp.client.MCPClient` 

192 directly via the container or code. 

193 """ 

194 

195 client_stdio_command: list[str] = field(default_factory=list) 

196 """Command and args to spawn a local MCP server as a subprocess client. 

197 

198 When non-empty, :class:`~lexigram.ai.mcp.client.MCPClient` is registered 

199 using :class:`~lexigram.ai.mcp.client.StdioClientTransport`. 

200 Takes precedence over ``client_url`` when both are set. 

201 """ 

202 

203 connectors: ConnectorsConfig = field(default_factory=ConnectorsConfig) 

204 """Optional built-in connector configuration. 

205 

206 Each connector is enabled by supplying a non-empty key value (e.g. a 

207 ``root_dir`` for the filesystem connector or a ``token`` for GitHub). 

208 """ 

209 

210 

211__all__ = ["MCPConfig"]