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

19 statements  

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

1"""MCP module for Lexigram.""" 

2 

3from __future__ import annotations 

4 

5from typing import TYPE_CHECKING, Any 

6 

7from lexigram.di.module import DynamicModule, Module, module 

8 

9if TYPE_CHECKING: 

10 from lexigram.ai.mcp.config import MCPConfig 

11 

12 

13@module(name="lexigram-ai-mcp") 

14class MCPModule(Module): 

15 """Module for MCP server. 

16 

17 Provides MCP server, handlers, and transports to the application. 

18 

19 Usage:: 

20 

21 app = LexigramApplication( 

22 modules=[..., MCPModule.configure()], 

23 ) 

24 

25 # With MCPController subclasses:: 

26 

27 app = LexigramApplication( 

28 modules=[..., MCPModule.configure(controllers=[DataToolsController])], 

29 ) 

30 

31 # Auto-expose existing services as MCP tools:: 

32 

33 app = LexigramApplication( 

34 modules=[ 

35 ..., 

36 MCPModule.from_services( 

37 services=[UserService, AnalyticsService], 

38 include_methods=["search", "get_*"], 

39 ), 

40 ], 

41 ) 

42 """ 

43 

44 @classmethod 

45 def from_services( 

46 cls, 

47 services: list[type], 

48 *, 

49 include_methods: list[str] | None = None, 

50 config: MCPConfig | None = None, 

51 ) -> DynamicModule: 

52 """Create an MCPModule that auto-exposes service methods as MCP tools. 

53 

54 Args: 

55 services: Service classes whose public methods are exposed as tools. 

56 include_methods: Optional glob patterns to restrict which methods are 

57 exposed (e.g. ``["search", "get_*"]``). 

58 config: Optional :class:`~lexigram.ai.mcp.config.MCPConfig` override. 

59 

60 Returns: 

61 A :class:`~lexigram.di.module.DynamicModule` descriptor. 

62 """ 

63 return cls.configure( 

64 config=config, 

65 services=services, 

66 include_methods=include_methods, 

67 ) 

68 

69 @classmethod 

70 def configure( 

71 cls, 

72 *, 

73 config: MCPConfig | None = None, 

74 controllers: list[type] | None = None, 

75 services: list[type] | None = None, 

76 include_methods: list[str] | None = None, 

77 enable_streaming: bool = True, 

78 **provider_kwargs: Any, 

79 ) -> DynamicModule: 

80 """Create an MCPModule with explicit provider configuration. 

81 

82 Args: 

83 config: Optional :class:`~lexigram.ai.mcp.config.MCPConfig`. 

84 controllers: :class:`~lexigram.ai.mcp.controllers.MCPController` 

85 subclasses to register with the server. 

86 services: Service classes to auto-expose as MCP tools. 

87 include_methods: Glob patterns to restrict auto-exposed methods. 

88 enable_streaming: Enable SSE streaming transport support. Defaults 

89 to ``True``; set to ``False`` to restrict the server to 

90 stdio-only transport. 

91 **provider_kwargs: Additional keyword arguments forwarded to 

92 :class:`~lexigram.ai.mcp.di.provider.MCPProvider`. 

93 

94 Returns: 

95 A :class:`~lexigram.di.module.DynamicModule` descriptor. 

96 """ 

97 from lexigram.ai.mcp.di.provider import MCPProvider 

98 from lexigram.ai.mcp.server import MCPServer 

99 

100 return DynamicModule( 

101 module=cls, 

102 providers=[ 

103 MCPProvider( 

104 config=config, 

105 controllers=controllers, 

106 services=services, 

107 include_methods=include_methods, 

108 enable_streaming=enable_streaming, 

109 **provider_kwargs, 

110 ) 

111 ], 

112 exports=[MCPServer], 

113 ) 

114 

115 @classmethod 

116 def stub(cls, config: MCPConfig | None = None) -> DynamicModule: 

117 """Create an MCPModule suitable for unit and integration testing. 

118 

119 Uses in-memory transport with no external MCP server connections. 

120 Streaming is disabled by default to simplify test assertions. 

121 

122 Args: 

123 config: Optional :class:`~lexigram.ai.mcp.config.MCPConfig` 

124 override. Uses safe test defaults when ``None``. 

125 

126 Returns: 

127 A :class:`~lexigram.di.module.DynamicModule` descriptor. 

128 """ 

129 from lexigram.ai.mcp.di.provider import MCPProvider 

130 from lexigram.ai.mcp.server import MCPServer 

131 

132 return DynamicModule( 

133 module=cls, 

134 providers=[MCPProvider(config=config, enable_streaming=False)], 

135 exports=[MCPServer], 

136 ) 

137 

138 

139__all__ = ["MCPModule"]