atlan-application-sdk 0.1.1rc44__py3-none-any.whl → 0.1.1rc45__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@ from typing import Any, Dict, List, Optional, Tuple, Type
4
4
  from application_sdk.activities import ActivitiesInterface
5
5
  from application_sdk.clients.base import BaseClient
6
6
  from application_sdk.clients.utils import get_workflow_client
7
+ from application_sdk.constants import ENABLE_MCP
7
8
  from application_sdk.events.models import EventRegistration
8
9
  from application_sdk.handlers.base import BaseHandler
9
10
  from application_sdk.observability.logger_adaptor import get_logger
@@ -29,7 +30,7 @@ class BaseApplication:
29
30
  self,
30
31
  name: str,
31
32
  server: Optional[ServerInterface] = None,
32
- application_manifest: Optional[dict] = None,
33
+ application_manifest: Optional[Dict[str, Any]] = None,
33
34
  client_class: Optional[Type[BaseClient]] = None,
34
35
  handler_class: Optional[Type[BaseHandler]] = None,
35
36
  ):
@@ -39,6 +40,9 @@ class BaseApplication:
39
40
  Args:
40
41
  name (str): The name of the application.
41
42
  server (ServerInterface): The server class for the application.
43
+ application_manifest (Optional[Dict[str, Any]]): Application manifest configuration.
44
+ client_class (Optional[Type[BaseClient]]): Client class for the application.
45
+ handler_class (Optional[Type[BaseHandler]]): Handler class for the application.
42
46
  """
43
47
  self.application_name = name
44
48
 
@@ -49,14 +53,21 @@ class BaseApplication:
49
53
 
50
54
  self.workflow_client = get_workflow_client(application_name=name)
51
55
 
52
- self.application_manifest: Dict[str, Any] = application_manifest
56
+ self.application_manifest: Optional[Dict[str, Any]] = application_manifest
53
57
  self.bootstrap_event_registration()
54
58
 
55
59
  self.client_class = client_class or BaseClient
56
60
  self.handler_class = handler_class or BaseHandler
57
61
 
62
+ # MCP configuration
63
+ self.mcp_server: Optional["MCPServer"] = None
64
+ if ENABLE_MCP:
65
+ from application_sdk.server.mcp import MCPServer
66
+
67
+ self.mcp_server = MCPServer(application_name=name)
68
+
58
69
  def bootstrap_event_registration(self):
59
- self.event_subscriptions = {}
70
+ self.event_subscriptions: Dict[str, EventWorkflowTrigger] = {}
60
71
  if self.application_manifest is None:
61
72
  logger.warning("No application manifest found, skipping event registration")
62
73
  return
@@ -122,8 +133,8 @@ class BaseApplication:
122
133
  ]
123
134
  workflow_activities = []
124
135
  for workflow_class, activities_class in workflow_and_activities_classes:
125
- workflow_activities.extend(
126
- workflow_class.get_activities(activities_class())
136
+ workflow_activities.extend( # type: ignore
137
+ workflow_class.get_activities(activities_class()) # type: ignore
127
138
  )
128
139
 
129
140
  self.worker = Worker(
@@ -134,6 +145,13 @@ class BaseApplication:
134
145
  activity_executor=activity_executor,
135
146
  )
136
147
 
148
+ # Register MCP tools if ENABLED_MCP is True and an MCP server is initialized
149
+ if self.mcp_server:
150
+ logger.info("Registering MCP tools from workflow and activities classes")
151
+ await self.mcp_server.register_tools( # type: ignore
152
+ workflow_and_activities_classes=workflow_and_activities_classes
153
+ )
154
+
137
155
  async def start_workflow(self, workflow_args, workflow_class) -> Any:
138
156
  """
139
157
  Start a new workflow execution.
@@ -147,7 +165,7 @@ class BaseApplication:
147
165
  """
148
166
  if self.workflow_client is None:
149
167
  raise ValueError("Workflow client not initialized")
150
- return await self.workflow_client.start_workflow(workflow_args, workflow_class)
168
+ return await self.workflow_client.start_workflow(workflow_args, workflow_class) # type: ignore
151
169
 
152
170
  async def start_worker(self, daemon: bool = True):
153
171
  """
@@ -162,39 +180,61 @@ class BaseApplication:
162
180
 
163
181
  async def setup_server(
164
182
  self,
165
- workflow_class,
183
+ workflow_class: Type[WorkflowInterface],
166
184
  ui_enabled: bool = True,
167
185
  has_configmap: bool = False,
168
186
  ):
169
187
  """
170
- Optionally set up a server for the application. (No-op by default)
188
+ Set up FastAPI server and automatically mount MCP if enabled.
189
+
190
+ Args:
191
+ workflow_class (WorkflowInterface): The workflow class for the application.
192
+ ui_enabled (bool): Whether to enable the UI.
193
+ has_configmap (bool): Whether to enable the configmap.
171
194
  """
172
195
  if self.workflow_client is None:
173
196
  await self.workflow_client.load()
174
197
 
175
- # Overrides the application server. serves the UI, and handles the various triggers
198
+ mcp_http_app: Optional[Any] = None
199
+ lifespan: Optional[Any] = None
200
+
201
+ if self.mcp_server:
202
+ try:
203
+ mcp_http_app = await self.mcp_server.get_http_app()
204
+ lifespan = mcp_http_app.lifespan
205
+ except Exception as e:
206
+ logger.warning(f"Failed to get MCP HTTP app: {e}")
207
+
176
208
  self.server = APIServer(
209
+ lifespan=lifespan,
177
210
  workflow_client=self.workflow_client,
178
211
  ui_enabled=ui_enabled,
179
212
  handler=self.handler_class(client=self.client_class()),
180
213
  has_configmap=has_configmap,
181
214
  )
182
215
 
216
+ # Mount MCP at root
217
+ if mcp_http_app:
218
+ try:
219
+ self.server.app.mount("", mcp_http_app) # Mount at root
220
+ except Exception as e:
221
+ logger.warning(f"Failed to mount MCP HTTP app: {e}")
222
+
223
+ # Register event-based workflows if any
183
224
  if self.event_subscriptions:
184
225
  for event_trigger in self.event_subscriptions.values():
185
- if event_trigger.workflow_class is None:
226
+ if event_trigger.workflow_class is None: # type: ignore
186
227
  raise ValueError(
187
228
  f"Workflow class not set for event trigger {event_trigger.event_id}"
188
229
  )
189
230
 
190
- self.server.register_workflow(
191
- workflow_class=event_trigger.workflow_class,
231
+ self.server.register_workflow( # type: ignore
232
+ workflow_class=event_trigger.workflow_class, # type: ignore
192
233
  triggers=[event_trigger],
193
234
  )
194
235
 
195
- # register the workflow on the application server
196
- # the workflow is by default triggered by an HTTP POST request to the /start endpoint
197
- self.server.register_workflow(
236
+ # Register the main workflow (HTTP POST /start endpoint)
237
+ self.server.register_workflow( # type: ignore
198
238
  workflow_class=workflow_class,
199
239
  triggers=[HttpWorkflowTrigger()],
200
240
  )
@@ -255,3 +255,9 @@ REDIS_SENTINEL_HOSTS = os.getenv("REDIS_SENTINEL_HOSTS", "")
255
255
  IS_LOCKING_DISABLED = os.getenv("IS_LOCKING_DISABLED", "true").lower() == "true"
256
256
  #: Retry interval for lock acquisition
257
257
  LOCK_RETRY_INTERVAL = int(os.getenv("LOCK_RETRY_INTERVAL", "5"))
258
+
259
+ # MCP Configuration
260
+ #: Flag to indicate if MCP should be enabled or not. Turning this to true will setup an MCP server along
261
+ #: with the application.
262
+ ENABLE_MCP = os.getenv("ENABLE_MCP", "false").lower() == "true"
263
+ MCP_METADATA_KEY = "__atlan_application_sdk_mcp_metadata"
@@ -0,0 +1,63 @@
1
+ """
2
+ MCP tool decorator for marking activities as MCP tools.
3
+
4
+ This module provides the @mcp_tool decorator that developers use to mark
5
+ activities for automatic exposure via Model Context Protocol.
6
+ """
7
+
8
+ from typing import Any, Callable, Optional
9
+
10
+ from application_sdk.constants import MCP_METADATA_KEY
11
+ from application_sdk.server.mcp import MCPMetadata
12
+
13
+
14
+ def mcp_tool(
15
+ name: Optional[str] = None,
16
+ description: Optional[str] = None,
17
+ visible: bool = True,
18
+ *args,
19
+ **kwargs,
20
+ ):
21
+ """
22
+ Decorator to mark functions as MCP tools.
23
+
24
+ Use this decorator to mark any function as an MCP tool. You can additionally use the `visible`
25
+ parameter to control whether the tool is visible at runtime or not.
26
+
27
+ Function parameters that are Pydantic models will be automatically converted into correct JSON schema
28
+ for the tool specification. This is handled by the underlying FastMCP server implementation.
29
+
30
+ Args:
31
+ name(Optional[str]): The name of the tool. Defaults to the function name.
32
+ description(Optional[str]): The description of the tool. Defaults to the function docstring.
33
+ visible(bool): Whether the MCP tool is visible at runtime or not. Defaults to True.
34
+ *args: Additional arguments to pass to the tool.
35
+ **kwargs: Additional keyword arguments to pass to the tool.
36
+
37
+ Examples:
38
+ >>> @mcp_tool(name="add_numbers", description="Add two numbers", visible=True)
39
+ >>> def add_numbers(self, a: int, b: int) -> int:
40
+ >>> return a + b
41
+
42
+
43
+ >>> # Use with Temporal activity decorator
44
+ >>> @activity.defn
45
+ >>> @mcp_tool(name="get_weather", description="Get the weather for a given city")
46
+ >>> async def get_weather(self, city: str) -> str:
47
+ >>> # ... activity implementation unchanged ...
48
+ """
49
+
50
+ def decorator(f: Callable[..., Any]) -> Callable[..., Any]:
51
+ mcp_metadata = MCPMetadata(
52
+ name=name if name else f.__name__,
53
+ description=description if description else f.__doc__,
54
+ visible=visible,
55
+ args=args,
56
+ kwargs=kwargs,
57
+ )
58
+
59
+ setattr(f, MCP_METADATA_KEY, mcp_metadata)
60
+
61
+ return f
62
+
63
+ return decorator
@@ -0,0 +1,4 @@
1
+ from application_sdk.server.mcp.models import MCPMetadata
2
+ from application_sdk.server.mcp.server import MCPServer
3
+
4
+ __all__ = ["MCPServer", "MCPMetadata"]
@@ -0,0 +1,11 @@
1
+ from typing import Any, Dict, Optional, Tuple
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class MCPMetadata(BaseModel):
7
+ name: str
8
+ description: Optional[str]
9
+ visible: bool
10
+ args: Tuple[Any, ...] = ()
11
+ kwargs: Dict[str, Any] = {}
@@ -0,0 +1,96 @@
1
+ """
2
+ MCP Server implementation using FastMCP for Atlan Application SDK.
3
+
4
+ This module provides the MCPServer class that automatically discovers
5
+ activities marked with @mcp_tool decorators and mounts them on FastAPI
6
+ using streamable HTTP transport.
7
+ """
8
+
9
+ from typing import Any, Callable, List, Optional, Tuple, Type
10
+
11
+ from fastmcp import FastMCP
12
+ from fastmcp.server.http import StarletteWithLifespan
13
+
14
+ from application_sdk.activities import ActivitiesInterface
15
+ from application_sdk.constants import MCP_METADATA_KEY
16
+ from application_sdk.observability.logger_adaptor import get_logger
17
+ from application_sdk.server.mcp.models import MCPMetadata
18
+ from application_sdk.workflows import WorkflowInterface
19
+
20
+
21
+ class MCPServer:
22
+ """
23
+ MCP Server using FastMCP 2.0 with FastAPI mounting capability.
24
+
25
+ This server automatically discovers activities marked with @mcp_tool
26
+ and creates a FastMCP server that can be mounted on FastAPI.
27
+ """
28
+
29
+ def __init__(self, application_name: str, instructions: Optional[str] = None):
30
+ """
31
+ Initialize the MCP server.
32
+
33
+ Args:
34
+ application_name (str): Name of the application
35
+ instructions (Optional[str]): Description for the MCP server
36
+ """
37
+ self.application_name = application_name
38
+
39
+ self.logger = get_logger(__name__)
40
+
41
+ # FastMCP Server
42
+ self.server = FastMCP(
43
+ name=f"{application_name} MCP",
44
+ instructions=instructions,
45
+ on_duplicate_tools="error",
46
+ )
47
+
48
+ async def register_tools(
49
+ self,
50
+ workflow_and_activities_classes: List[
51
+ Tuple[Type[WorkflowInterface], Type[ActivitiesInterface]]
52
+ ],
53
+ ) -> None:
54
+ """
55
+ Discover activities marked with @mcp_tool and register them.
56
+
57
+ Args:
58
+ workflow_and_activities_classes: List of (workflow_class, activities_class) tuples
59
+ """
60
+ activity_methods: List[Callable[..., Any]] = []
61
+ for workflow_class, activities_class in workflow_and_activities_classes:
62
+ activities_instance = activities_class()
63
+ activity_methods.extend(workflow_class.get_activities(activities_instance)) # type: ignore
64
+
65
+ for f in activity_methods:
66
+ mcp_metadata: Optional[MCPMetadata] = getattr(f, MCP_METADATA_KEY, None)
67
+ if not mcp_metadata:
68
+ self.logger.info(
69
+ f"No MCP metadata found on activity method {f.__name__}. Skipping tool registration"
70
+ )
71
+ continue
72
+
73
+ if mcp_metadata.visible:
74
+ self.logger.info(
75
+ f"Registering tool {mcp_metadata.name} with description: {mcp_metadata.description}"
76
+ )
77
+ self.server.tool(
78
+ f,
79
+ name=mcp_metadata.name,
80
+ description=mcp_metadata.description,
81
+ *mcp_metadata.args,
82
+ **mcp_metadata.kwargs,
83
+ )
84
+ else:
85
+ self.logger.info(
86
+ f"Tool {mcp_metadata.name} is marked as not visible. Skipping tool registration"
87
+ )
88
+
89
+ tools = await self.server.get_tools()
90
+ self.logger.info(f"Registered {len(tools)} tools: {list(tools.keys())}")
91
+
92
+ async def get_http_app(self) -> StarletteWithLifespan:
93
+ """
94
+ Get the HTTP app for the MCP server.
95
+ """
96
+ return self.server.http_app()
@@ -2,4 +2,4 @@
2
2
  Version information for the application_sdk package.
3
3
  """
4
4
 
5
- __version__ = "0.1.1rc44"
5
+ __version__ = "0.1.1rc45"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atlan-application-sdk
3
- Version: 0.1.1rc44
3
+ Version: 0.1.1rc45
4
4
  Summary: Atlan Application SDK is a Python library for developing applications on the Atlan Platform
5
5
  Project-URL: Repository, https://github.com/atlanhq/application-sdk
6
6
  Project-URL: Documentation, https://github.com/atlanhq/application-sdk/README.md
@@ -1,6 +1,6 @@
1
1
  application_sdk/__init__.py,sha256=2e2mvmLJ5dxmJGPELtb33xwP-j6JMdoIuqKycEn7hjg,151
2
- application_sdk/constants.py,sha256=1THiejjOEgm4kHFN-PrwrUkfRk7q1pjOLWLm-t2ph1Q,10674
3
- application_sdk/version.py,sha256=PUC8knGCYDRf-xg5lgaXJ_F5evubFFRTEkWX9EULiq0,88
2
+ application_sdk/constants.py,sha256=eLHmH9GukXCKK-u5a4bAqz8BeCOCusCM0eW0Q0Bwjns,10947
3
+ application_sdk/version.py,sha256=p3JLlDmx39Ch7lMX6DS5ZKpVpATBikUiznrMlVxdpTA,88
4
4
  application_sdk/worker.py,sha256=i5f0AeKI39IfsLO05QkwC6uMz0zDPSJqP7B2byri1VI,7489
5
5
  application_sdk/activities/__init__.py,sha256=QaXLOBYbb0zPOY5kfDQh56qbXQFaYNXOjJ5PCvatiZ4,9530
6
6
  application_sdk/activities/lock_management.py,sha256=L__GZ9BsArwU1ntYwAgCKsSjCqN6QBeOfT-OT4WyD4Y,3983
@@ -14,7 +14,7 @@ application_sdk/activities/metadata_extraction/rest.py,sha256=47DEQpj8HBSa-_TImW
14
14
  application_sdk/activities/metadata_extraction/sql.py,sha256=ivIbTrkKAonijQQPfiOigoiXLWtA_-nLUn9lz09lpaU,34725
15
15
  application_sdk/activities/query_extraction/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  application_sdk/activities/query_extraction/sql.py,sha256=l64cGyTmbtaGcg3qj1YXKyNWiWeRsWPEuQyqW06rxxQ,21165
17
- application_sdk/application/__init__.py,sha256=PbSImXYaQQ2IIee2SvI8AjDiSo2QcCFrM1PX3x-_RQs,8035
17
+ application_sdk/application/__init__.py,sha256=hb5zBc4zi-10av8Ivbovhb0CEAwNgr3eFlfpRaMKVmI,9861
18
18
  application_sdk/application/metadata_extraction/sql.py,sha256=rOd06Wodr4GyzupCYxVSCsNcuNar1rJM66ej9vocNHw,8138
19
19
  application_sdk/clients/__init__.py,sha256=C9T84J7V6ZumcoWJPAxdd3tqSmbyciaGBJn-CaCCny0,1341
20
20
  application_sdk/clients/atlan.py,sha256=l6yV39fr1006SJFwkOTNDQlbSFlHCZQaUPfdUlzdVEg,5053
@@ -36,6 +36,7 @@ application_sdk/common/utils.py,sha256=ImCrlyCj5Mj571CVWfqy5MynVVju9xhn1ItSlJoae
36
36
  application_sdk/common/.cursor/BUGBOT.md,sha256=OkB5TMAEJFzaBfbNb3g9ZDPW2r1krQE_KEuJbytMPuI,12176
37
37
  application_sdk/decorators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
38
  application_sdk/decorators/locks.py,sha256=-cdbICCMns3lkqZ4CCQabW1du8cEu9XSWlwzWTTbIPk,1411
39
+ application_sdk/decorators/mcp_tool.py,sha256=uxuc0Qk1A_MMvnwWe19kYGn4cfipvwwkmITR5nBdHP8,2215
39
40
  application_sdk/decorators/.cursor/BUGBOT.md,sha256=iiS_41FKaJ4-L2jm9ziEqQhQNGYGNZzHVVk09c2cgN8,11250
40
41
  application_sdk/docgen/__init__.py,sha256=Gr_3uVEnSspKd_-R1YRsDABI-iP4170Dvg5jM2oD76A,7352
41
42
  application_sdk/docgen/exporters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -89,6 +90,9 @@ application_sdk/server/fastapi/middleware/logmiddleware.py,sha256=CxcPtDmCbSfSZ8
89
90
  application_sdk/server/fastapi/middleware/metrics.py,sha256=5ddHAIg5sT-u9tB_HHMGL3Cfu2g1rm9z7ksienIr9ks,1563
90
91
  application_sdk/server/fastapi/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
92
  application_sdk/server/fastapi/routers/server.py,sha256=vfHQwZCysThzfeVFNVW1IjuAdL0c1Cs4fULKTBK2eNo,4209
93
+ application_sdk/server/mcp/__init__.py,sha256=29HI-GkNn9wOPsb86Litmv0MhXWFBNVBOivJcNFWZas,154
94
+ application_sdk/server/mcp/models.py,sha256=3jX_3wXBuFs6XX9OG-xoag8VkKYWO_Y5r5-CHcLU5vg,236
95
+ application_sdk/server/mcp/server.py,sha256=HG8tFmcc-f9Wj3vZzs2oRoNJzN1s5hwjnKykSdTXgCQ,3450
92
96
  application_sdk/services/__init__.py,sha256=H-5HZEPdr53MUfAggyHqHhRXDRLZFZsxvJgWbr257Ds,465
93
97
  application_sdk/services/atlan_storage.py,sha256=TKzXxu0yXeUcmZehwp8PcnQTC4A9w9RlZ0Fl-Xp1bLE,8509
94
98
  application_sdk/services/eventstore.py,sha256=X03JzodKByXh8w8nOl658rnnZfMFTj0IkmiLVbd6IN8,6729
@@ -152,8 +156,8 @@ application_sdk/workflows/metadata_extraction/__init__.py,sha256=jHUe_ZBQ66jx8bg
152
156
  application_sdk/workflows/metadata_extraction/sql.py,sha256=6ZaVt84n-8U2ZvR9GR7uIJKv5v8CuyQjhlnoRJvDszc,12435
153
157
  application_sdk/workflows/query_extraction/__init__.py,sha256=n066_CX5RpJz6DIxGMkKS3eGSRg03ilaCtsqfJWQb7Q,117
154
158
  application_sdk/workflows/query_extraction/sql.py,sha256=kT_JQkLCRZ44ZpaC4QvPL6DxnRIIVh8gYHLqRbMI-hA,4826
155
- atlan_application_sdk-0.1.1rc44.dist-info/METADATA,sha256=FoTta0zU5XJfLfr0hmUeEyEXFOqZ0-Plb_CerNBVfcM,5567
156
- atlan_application_sdk-0.1.1rc44.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
157
- atlan_application_sdk-0.1.1rc44.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
158
- atlan_application_sdk-0.1.1rc44.dist-info/licenses/NOTICE,sha256=A-XVVGt3KOYuuMmvSMIFkg534F1vHiCggEBp4Ez3wGk,1041
159
- atlan_application_sdk-0.1.1rc44.dist-info/RECORD,,
159
+ atlan_application_sdk-0.1.1rc45.dist-info/METADATA,sha256=nuX1LdMTNoLQW3eKYU9KdNp2vXPiLG3RnKxZeu5ZcpM,5567
160
+ atlan_application_sdk-0.1.1rc45.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
161
+ atlan_application_sdk-0.1.1rc45.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
162
+ atlan_application_sdk-0.1.1rc45.dist-info/licenses/NOTICE,sha256=A-XVVGt3KOYuuMmvSMIFkg534F1vHiCggEBp4Ez3wGk,1041
163
+ atlan_application_sdk-0.1.1rc45.dist-info/RECORD,,