codemie-mcp-datasources 0.1000.7__tar.gz
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.
@@ -0,0 +1,13 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: codemie-mcp-datasources
|
3
|
+
Version: 0.1000.7
|
4
|
+
Summary: MCP server for connecting to CodeMie Datasources
|
5
|
+
Author: Nikita Levyankov
|
6
|
+
Author-email: nikita_levyankov@epam.com
|
7
|
+
Requires-Python: >=3.12,<4.0
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
10
|
+
Requires-Dist: certifi (>=2025.1.31,<2026.0.0)
|
11
|
+
Requires-Dist: codemie-sdk-python-az (==0.1000.7)
|
12
|
+
Requires-Dist: httpx (>=0.27.0,<0.28.0)
|
13
|
+
Requires-Dist: mcp (>=1.2.0,<2.0.0)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
[project]
|
2
|
+
name = "codemie-mcp-datasources-az"
|
3
|
+
version = "0.1.2"
|
4
|
+
description = "MCP server for connecting to CodeMie Datasources"
|
5
|
+
#readme = "README.md"
|
6
|
+
packages = [
|
7
|
+
{ include = "datasources_mcp", from = "src" }
|
8
|
+
]
|
9
|
+
|
10
|
+
[tool.poetry]
|
11
|
+
name = "codemie-mcp-datasources"
|
12
|
+
version = "0.1000.7"
|
13
|
+
description = "MCP server for connecting to CodeMie Datasources"
|
14
|
+
authors = [
|
15
|
+
"Nikita Levyankov <nikita_levyankov@epam.com>",
|
16
|
+
]
|
17
|
+
#readme = "README.md"
|
18
|
+
packages = [
|
19
|
+
{ include = "datasources_mcp", from = "src" }
|
20
|
+
]
|
21
|
+
|
22
|
+
[tool.poetry.dependencies]
|
23
|
+
python = "^3.12"
|
24
|
+
mcp = "^1.2.0"
|
25
|
+
httpx = "^0.27.0"
|
26
|
+
codemie-sdk-python-az = "0.1000.7"
|
27
|
+
certifi = "^2025.1.31"
|
28
|
+
|
29
|
+
[tool.poetry.group.dev.dependencies]
|
30
|
+
pytest = "^8.0.0"
|
31
|
+
black = "^24.1.0"
|
32
|
+
isort = "^5.13.2"
|
33
|
+
|
34
|
+
[build-system]
|
35
|
+
requires = ["poetry-core"]
|
36
|
+
build-backend = "poetry.core.masonry.api"
|
37
|
+
|
38
|
+
[tool.poetry.scripts]
|
39
|
+
codemie-mcp-assistant = "src.datasources_mcp:main"
|
@@ -0,0 +1,265 @@
|
|
1
|
+
"""CodeMie MCP implementation for datasources."""
|
2
|
+
|
3
|
+
import os
|
4
|
+
from typing import Any, Dict, List, Optional, Literal
|
5
|
+
|
6
|
+
from codemie_sdk import CodeMieClient
|
7
|
+
from codemie_sdk.models.datasource import (
|
8
|
+
DataSourceType,
|
9
|
+
DataSourceStatus,
|
10
|
+
CodeDataSourceRequest,
|
11
|
+
UpdateCodeDataSourceRequest,
|
12
|
+
)
|
13
|
+
from mcp.server.fastmcp import FastMCP
|
14
|
+
|
15
|
+
DEFAULT_AUTH_CLIENT_ID = "codemie-sdk"
|
16
|
+
DEFAULT_AUTH_REALM_NAME = "codemie-prod"
|
17
|
+
DEFAULT_AUTH_SERVER_URL = (
|
18
|
+
"https://keycloak.eks-core.aws.main.edp.projects.epam.com/auth"
|
19
|
+
)
|
20
|
+
DEFAULT_CODEMIE_API_DOMAIN = "https://codemie.lab.epam.com/code-assistant-api"
|
21
|
+
|
22
|
+
# Initialize FastMCP server
|
23
|
+
mcp = FastMCP("codemie-datasources")
|
24
|
+
|
25
|
+
# Client instance
|
26
|
+
_client: Optional[CodeMieClient] = None
|
27
|
+
|
28
|
+
|
29
|
+
def get_client() -> CodeMieClient:
|
30
|
+
"""Gets authenticated CodeMie client instance."""
|
31
|
+
username = os.getenv("CODEMIE_USERNAME")
|
32
|
+
password = os.getenv("CODEMIE_PASSWORD")
|
33
|
+
auth_client_id = os.getenv("CODEMIE_AUTH_CLIENT_ID", DEFAULT_AUTH_CLIENT_ID)
|
34
|
+
auth_realm_name = os.getenv("CODEMIE_AUTH_REALM_NAME", DEFAULT_AUTH_REALM_NAME)
|
35
|
+
auth_server_url = os.getenv("CODEMIE_AUTH_SERVER_URL", DEFAULT_AUTH_SERVER_URL)
|
36
|
+
codemie_api_domain = os.getenv("CODEMIE_API_DOMAIN", DEFAULT_CODEMIE_API_DOMAIN)
|
37
|
+
|
38
|
+
if not username or not password:
|
39
|
+
raise ValueError(
|
40
|
+
"Username and password must be set via environment variables: CODEMIE_USERNAME, CODEMIE_PASSWORD"
|
41
|
+
)
|
42
|
+
|
43
|
+
return CodeMieClient(
|
44
|
+
username=username,
|
45
|
+
password=password,
|
46
|
+
verify_ssl=False,
|
47
|
+
auth_client_id=auth_client_id,
|
48
|
+
auth_realm_name=auth_realm_name,
|
49
|
+
auth_server_url=auth_server_url,
|
50
|
+
codemie_api_domain=codemie_api_domain,
|
51
|
+
)
|
52
|
+
|
53
|
+
|
54
|
+
@mcp.tool()
|
55
|
+
async def list_datasources(
|
56
|
+
page: int = 0,
|
57
|
+
per_page: int = 10,
|
58
|
+
sort_key: Literal["date", "update_date"] = "update_date",
|
59
|
+
sort_order: Literal["asc", "desc"] = "desc",
|
60
|
+
datasource_types: List[DataSourceType] = None,
|
61
|
+
projects: List[str] = None,
|
62
|
+
owner: str = None,
|
63
|
+
status: DataSourceStatus = None,
|
64
|
+
) -> List[Dict[str, Any]]:
|
65
|
+
"""Get list of available datasources.
|
66
|
+
|
67
|
+
Args:
|
68
|
+
sort_key: sort results by params
|
69
|
+
sort_order: sort results in order
|
70
|
+
page: Page number (default: 0)
|
71
|
+
per_page: Items per page (default: 10)
|
72
|
+
projects: Filter by project name
|
73
|
+
datasource_types: Filter by datasource type
|
74
|
+
owner: Filter by owner
|
75
|
+
status: Filter by status
|
76
|
+
"""
|
77
|
+
client = get_client()
|
78
|
+
|
79
|
+
datasources = client.datasources.list(
|
80
|
+
page=page,
|
81
|
+
per_page=per_page,
|
82
|
+
sort_key=sort_key,
|
83
|
+
sort_order=sort_order,
|
84
|
+
datasource_types=datasource_types,
|
85
|
+
projects=projects,
|
86
|
+
owner=owner,
|
87
|
+
status=status,
|
88
|
+
)
|
89
|
+
|
90
|
+
# Convert to simplified dict format
|
91
|
+
return [
|
92
|
+
{
|
93
|
+
"id": ds.id,
|
94
|
+
"name": ds.name,
|
95
|
+
"description": ds.description,
|
96
|
+
"type": ds.type.value,
|
97
|
+
"project": ds.project_name,
|
98
|
+
"status": ds.status.value,
|
99
|
+
"created_date": ds.created_date.isoformat() if ds.created_date else None,
|
100
|
+
"update_date": ds.update_date.isoformat() if ds.update_date else None,
|
101
|
+
"error_message": ds.error_message,
|
102
|
+
"processing_info": ds.processing_info.model_dump()
|
103
|
+
if ds.processing_info
|
104
|
+
else None,
|
105
|
+
}
|
106
|
+
for ds in datasources
|
107
|
+
]
|
108
|
+
|
109
|
+
|
110
|
+
@mcp.tool()
|
111
|
+
async def get_datasource(datasource_id: str) -> Dict[str, Any]:
|
112
|
+
"""Get datasource details by ID.
|
113
|
+
|
114
|
+
Args:
|
115
|
+
datasource_id: ID of the datasource to retrieve
|
116
|
+
"""
|
117
|
+
client = get_client()
|
118
|
+
datasource = client.datasources.get(datasource_id)
|
119
|
+
|
120
|
+
return {
|
121
|
+
"id": datasource.id,
|
122
|
+
"name": datasource.name,
|
123
|
+
"description": datasource.description,
|
124
|
+
"type": datasource.type.value,
|
125
|
+
"project": datasource.project_name,
|
126
|
+
"status": datasource.status.value,
|
127
|
+
"created_date": datasource.created_date.isoformat()
|
128
|
+
if datasource.created_date
|
129
|
+
else None,
|
130
|
+
"update_date": datasource.update_date.isoformat()
|
131
|
+
if datasource.update_date
|
132
|
+
else None,
|
133
|
+
"error_message": datasource.error_message,
|
134
|
+
"processing_info": datasource.processing_info.model_dump()
|
135
|
+
if datasource.processing_info
|
136
|
+
else None,
|
137
|
+
"code": datasource.code.model_dump() if datasource.code else None,
|
138
|
+
"jira": datasource.jira.model_dump() if datasource.jira else None,
|
139
|
+
"confluence": datasource.confluence.model_dump()
|
140
|
+
if datasource.confluence
|
141
|
+
else None,
|
142
|
+
}
|
143
|
+
|
144
|
+
|
145
|
+
@mcp.tool()
|
146
|
+
async def create_code_datasource(
|
147
|
+
name: str,
|
148
|
+
description: str,
|
149
|
+
project_name: str,
|
150
|
+
repository_link: str,
|
151
|
+
branch: str,
|
152
|
+
index_type: str = "code",
|
153
|
+
files_filter: str = "",
|
154
|
+
embeddings_model: Optional[str] = None,
|
155
|
+
shared_with_project: bool = False,
|
156
|
+
) -> Dict[str, Any]:
|
157
|
+
"""Create a new code datasource.
|
158
|
+
|
159
|
+
Args:
|
160
|
+
name: Datasource name (lowercase letters and underscores only)
|
161
|
+
description: Datasource description
|
162
|
+
project_name: Project name
|
163
|
+
repository_link: Git repository URL
|
164
|
+
branch: Git branch name
|
165
|
+
index_type: Type of indexing (code, summary, chunk-summary)
|
166
|
+
files_filter: File patterns to include/exclude
|
167
|
+
embeddings_model: Model for embeddings generation
|
168
|
+
shared_with_project: Whether datasource is shared with project
|
169
|
+
"""
|
170
|
+
client = get_client()
|
171
|
+
|
172
|
+
request = CodeDataSourceRequest(
|
173
|
+
name=name,
|
174
|
+
description=description,
|
175
|
+
project_name=project_name,
|
176
|
+
link=repository_link,
|
177
|
+
branch=branch,
|
178
|
+
index_type=index_type,
|
179
|
+
files_filter=files_filter,
|
180
|
+
embeddings_model=embeddings_model,
|
181
|
+
shared_with_project=shared_with_project,
|
182
|
+
)
|
183
|
+
|
184
|
+
datasource = client.datasources.create(request)
|
185
|
+
return get_datasource(datasource.id)
|
186
|
+
|
187
|
+
|
188
|
+
@mcp.tool()
|
189
|
+
async def delete_datasource(datasource_id: str) -> Dict[str, Any]:
|
190
|
+
"""Delete a datasource.
|
191
|
+
|
192
|
+
Args:
|
193
|
+
datasource_id: ID of the datasource to delete
|
194
|
+
"""
|
195
|
+
client = get_client()
|
196
|
+
return client.datasources.delete(datasource_id)
|
197
|
+
|
198
|
+
|
199
|
+
@mcp.tool()
|
200
|
+
async def update_code_datasource(
|
201
|
+
datasource_id: str,
|
202
|
+
name: Optional[str] = None,
|
203
|
+
description: Optional[str] = None,
|
204
|
+
branch: Optional[str] = None,
|
205
|
+
files_filter: Optional[str] = None,
|
206
|
+
full_reindex: Optional[bool] = None,
|
207
|
+
skip_reindex: Optional[bool] = None,
|
208
|
+
resume_indexing: Optional[bool] = None,
|
209
|
+
) -> Dict[str, Any]:
|
210
|
+
"""Update a code datasource.
|
211
|
+
|
212
|
+
Args:
|
213
|
+
datasource_id: ID of the datasource to update
|
214
|
+
name: New name (optional)
|
215
|
+
description: New description (optional)
|
216
|
+
branch: New branch (optional)
|
217
|
+
files_filter: New files filter (optional)
|
218
|
+
full_reindex: Whether to perform full reindex
|
219
|
+
skip_reindex: Whether to skip reindex
|
220
|
+
resume_indexing: Whether to resume indexing
|
221
|
+
"""
|
222
|
+
client = get_client()
|
223
|
+
|
224
|
+
# First get current datasource
|
225
|
+
current = client.datasources.get(datasource_id)
|
226
|
+
|
227
|
+
# Prepare update request
|
228
|
+
request = UpdateCodeDataSourceRequest(
|
229
|
+
name=name or current.name,
|
230
|
+
description=description or current.description,
|
231
|
+
project_name=current.project_name,
|
232
|
+
branch=branch or current.code.branch if current.code else None,
|
233
|
+
files_filter=files_filter or current.code.files_filter
|
234
|
+
if current.code
|
235
|
+
else None,
|
236
|
+
full_reindex=full_reindex,
|
237
|
+
skip_reindex=skip_reindex,
|
238
|
+
resume_indexing=resume_indexing,
|
239
|
+
)
|
240
|
+
|
241
|
+
datasource = client.datasources.update(datasource_id, request)
|
242
|
+
return get_datasource(datasource.id)
|
243
|
+
|
244
|
+
|
245
|
+
@mcp.tool()
|
246
|
+
async def get_datasource_processing_info(datasource_id: str) -> Dict[str, Any]:
|
247
|
+
"""Get datasource processing information.
|
248
|
+
|
249
|
+
Args:
|
250
|
+
datasource_id: ID of the datasource to get info for
|
251
|
+
"""
|
252
|
+
client = get_client()
|
253
|
+
info = client.datasources.get_processing_info(datasource_id)
|
254
|
+
return {
|
255
|
+
"status": info.status.value if info.status else None,
|
256
|
+
"total_files": info.total_files,
|
257
|
+
"processed_files": info.processed_files,
|
258
|
+
"progress": info.progress,
|
259
|
+
"error_message": info.error_message,
|
260
|
+
}
|
261
|
+
|
262
|
+
|
263
|
+
if __name__ == "__main__":
|
264
|
+
# Initialize and run the server
|
265
|
+
mcp.run(transport="stdio")
|