codemie-mcp-assistants 0.1.3__py3-none-any.whl → 0.1.5__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.
Potentially problematic release.
This version of codemie-mcp-assistants might be problematic. Click here for more details.
- codemie_mcp_assistants-0.1.5.dist-info/METADATA +128 -0
- codemie_mcp_assistants-0.1.5.dist-info/RECORD +6 -0
- codemie_mcp_assistants-0.1.5.dist-info/entry_points.txt +3 -0
- src/model.py +114 -0
- src/server.py +195 -0
- assistants_mcp/__init__.py +0 -0
- assistants_mcp/assistants_mcp.py +0 -151
- codemie_mcp_assistants-0.1.3.dist-info/METADATA +0 -13
- codemie_mcp_assistants-0.1.3.dist-info/RECORD +0 -6
- codemie_mcp_assistants-0.1.3.dist-info/entry_points.txt +0 -3
- {codemie_mcp_assistants-0.1.3.dist-info → codemie_mcp_assistants-0.1.5.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: codemie-mcp-assistants
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: MCP server for connecting to CodeMie Assistants API
|
|
5
|
+
Author: Vadym Vlasenko
|
|
6
|
+
Author-email: vadym_vlasenko@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 (==0.1.5)
|
|
12
|
+
Requires-Dist: httpx (>=0.27.0,<0.28.0)
|
|
13
|
+
Requires-Dist: mcp (>=1.6.0,<2.0.0)
|
|
14
|
+
Requires-Dist: pydantic (>=2.11.2,<3.0.0)
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# CodeMie Assistants MCP Server
|
|
18
|
+
|
|
19
|
+
Python server implementing Model Context Protocol (MCP) for CodeMie Assistants operations.
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
- Chat with AI/Run CodeMie assistant
|
|
23
|
+
|
|
24
|
+
Note: The server requires authentication credentials via environment variables.
|
|
25
|
+
|
|
26
|
+
## API
|
|
27
|
+
|
|
28
|
+
### Tools
|
|
29
|
+
|
|
30
|
+
#### chat
|
|
31
|
+
Chat with a specific AI assistant
|
|
32
|
+
Inputs:
|
|
33
|
+
- `message (string)`: Message to send to assistant
|
|
34
|
+
- `conversation_id (string)`: Identifier of current conversation. It should be always passed if present in current communication thread.
|
|
35
|
+
- `history (array, optional)`: Previous conversation messages in format:
|
|
36
|
+
[{"role": "user|assistant", "message": "text"}]
|
|
37
|
+
|
|
38
|
+
Returns generated assistant response as text
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
Ensure you have `Python 3.12` or later installed.
|
|
43
|
+
|
|
44
|
+
### Using uv (recommended)
|
|
45
|
+
|
|
46
|
+
When using [`uv`](https://docs.astral.sh/uv/) no specific installation is needed. We will
|
|
47
|
+
use [`uvx`](https://docs.astral.sh/uv/guides/tools/) to directly run *codemie-mcp-assistants*.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
uvx codemie-mcp-assistants
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Using Poetry
|
|
54
|
+
|
|
55
|
+
Alternatively you can install via Poetry:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
poetry install codemie-mcp-assistants
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
After installation, you can run it as a script using:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
poetry run codemie-mcp-assistants
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Configuration
|
|
68
|
+
|
|
69
|
+
### Configure for Claude.app
|
|
70
|
+
|
|
71
|
+
Add to your Claude settings:
|
|
72
|
+
|
|
73
|
+
<details>
|
|
74
|
+
<summary>Using uvx</summary>
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
"mcpServers": {
|
|
78
|
+
"codemie": {
|
|
79
|
+
"command": "uvx",
|
|
80
|
+
"args": ["codemie-mcp-assistants"],
|
|
81
|
+
"env": {
|
|
82
|
+
"CODEMIE_ASSISTANT_ID": "your-assistant-id"
|
|
83
|
+
"CODEMIE_USERNAME": "your-username",
|
|
84
|
+
"CODEMIE_PASSWORD": "your-password"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
</details>
|
|
90
|
+
|
|
91
|
+
<details>
|
|
92
|
+
<summary>Using poetry installation</summary>
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
"mcpServers": {
|
|
96
|
+
"codemie": {
|
|
97
|
+
"command": "poetry",
|
|
98
|
+
"args": ["run", "codemie-mcp-assistants"],
|
|
99
|
+
"env": {
|
|
100
|
+
"CODEMIE_ASSISTANT_ID": "your-assistant-id"
|
|
101
|
+
"CODEMIE_USERNAME": "your-username",
|
|
102
|
+
"CODEMIE_PASSWORD": "your-password"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
</details>
|
|
108
|
+
|
|
109
|
+
### Environment Variables
|
|
110
|
+
|
|
111
|
+
- `CODEMIE_ASSISTANT_ID`: "AI/Run CodeMie assistant UID"
|
|
112
|
+
|
|
113
|
+
The following environment variables are required for authentication:
|
|
114
|
+
|
|
115
|
+
- `CODEMIE_USERNAME`: Your CodeMie username
|
|
116
|
+
- `CODEMIE_PASSWORD`: Your CodeMie password
|
|
117
|
+
|
|
118
|
+
Optional configuration:
|
|
119
|
+
- `CODEMIE_AUTH_CLIENT_ID`: Auth client ID (default: "codemie-sdk")
|
|
120
|
+
- `CODEMIE_AUTH_REALM_NAME`: Auth realm name (default: "codemie-prod")
|
|
121
|
+
|
|
122
|
+
## Build
|
|
123
|
+
|
|
124
|
+
### Make build:
|
|
125
|
+
```bash
|
|
126
|
+
make build
|
|
127
|
+
```
|
|
128
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
src/model.py,sha256=_mPLj0Kvr6eQJUPRio7i69dxv1BEB1DYeDe4aHpQxBk,4304
|
|
2
|
+
src/server.py,sha256=4YF-83T5v3lf2MnD2O4K_PBpc4AYePPiFqY9NHJdjYs,7081
|
|
3
|
+
codemie_mcp_assistants-0.1.5.dist-info/METADATA,sha256=liBsK447AMCG0ItvrqUjmpGedICrytlm90xh3DEq-iU,2983
|
|
4
|
+
codemie_mcp_assistants-0.1.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
5
|
+
codemie_mcp_assistants-0.1.5.dist-info/entry_points.txt,sha256=dbYga2pCSPONVT8eWt8_4ytDhgBn2EhCucS2-b_E9cM,66
|
|
6
|
+
codemie_mcp_assistants-0.1.5.dist-info/RECORD,,
|
src/model.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field, model_validator
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AssistantInfo(BaseModel):
|
|
8
|
+
"""Model representing assistant information from the MCP API"""
|
|
9
|
+
|
|
10
|
+
id: str = Field(..., description="Unique identifier of the assistant")
|
|
11
|
+
name: str = Field(..., description="Name of the assistant")
|
|
12
|
+
slug: Optional[str] = Field(None, description="Slug of the assistant")
|
|
13
|
+
description: str = Field(..., description="Description of the assistant")
|
|
14
|
+
project: Optional[str] = Field(None, description="Associated project name")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AssistantNotFoundError(Exception):
|
|
18
|
+
"""Exception raised when an assistant is not found"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, assistant_id: str):
|
|
21
|
+
self.assistant_id = assistant_id
|
|
22
|
+
self.message = f"Assistant with ID {assistant_id} not found"
|
|
23
|
+
super().__init__(self.message)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AuthConfig(BaseModel):
|
|
27
|
+
"""Authentication configuration with support for multiple auth methods."""
|
|
28
|
+
|
|
29
|
+
username: Optional[str] = Field(
|
|
30
|
+
default=None, description="Username for password-based authentication"
|
|
31
|
+
)
|
|
32
|
+
password: Optional[str] = Field(
|
|
33
|
+
default=None, description="Password for password-based authentication"
|
|
34
|
+
)
|
|
35
|
+
client_id: str = Field(
|
|
36
|
+
default="codemie-sdk",
|
|
37
|
+
description="Client ID for client credentials authentication",
|
|
38
|
+
)
|
|
39
|
+
client_secret: Optional[str] = Field(
|
|
40
|
+
default=None, description="Client secret for client credentials authentication"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
@model_validator(mode="after")
|
|
44
|
+
def validate_auth_method(self) -> "AuthConfig":
|
|
45
|
+
has_user_pass = bool(self.username and self.password)
|
|
46
|
+
has_client_creds = bool(self.client_id and self.client_secret)
|
|
47
|
+
|
|
48
|
+
if not (has_user_pass or has_client_creds):
|
|
49
|
+
raise ValueError(
|
|
50
|
+
"Authentication configuration missing. Please provide either:\n"
|
|
51
|
+
"- CODEMIE_USERNAME and CODEMIE_PASSWORD for password authentication\n"
|
|
52
|
+
"- CODEMIE_AUTH_CLIENT_ID and CODEMIE_AUTH_CLIENT_SECRET for client credentials\n"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if has_user_pass and has_client_creds:
|
|
56
|
+
import warnings
|
|
57
|
+
|
|
58
|
+
warnings.warn(
|
|
59
|
+
"Both authentication methods provided. Using client credentials authentication.",
|
|
60
|
+
UserWarning,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return self
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Settings:
|
|
67
|
+
"""Application settings configured via environment variables."""
|
|
68
|
+
|
|
69
|
+
# Default values
|
|
70
|
+
DEFAULT_AUTH_CLIENT_ID = "codemie-sdk"
|
|
71
|
+
DEFAULT_AUTH_REALM_NAME = "codemie-prod"
|
|
72
|
+
DEFAULT_AUTH_SERVER_URL = (
|
|
73
|
+
"https://keycloak.eks-core.aws.main.edp.projects.epam.com/auth"
|
|
74
|
+
)
|
|
75
|
+
DEFAULT_CODEMIE_API_DOMAIN = "https://codemie.lab.epam.com/code-assistant-api"
|
|
76
|
+
|
|
77
|
+
def __init__(self):
|
|
78
|
+
# Authentication settings
|
|
79
|
+
self.auth = AuthConfig(
|
|
80
|
+
username=os.environ.get("CODEMIE_USERNAME"),
|
|
81
|
+
password=os.environ.get("CODEMIE_PASSWORD"),
|
|
82
|
+
client_id=os.environ.get(
|
|
83
|
+
"CODEMIE_AUTH_CLIENT_ID", self.DEFAULT_AUTH_CLIENT_ID
|
|
84
|
+
),
|
|
85
|
+
client_secret=os.environ.get("CODEMIE_AUTH_CLIENT_SECRET"),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Server configuration
|
|
89
|
+
self.auth_client_id = os.environ.get(
|
|
90
|
+
"CODEMIE_AUTH_CLIENT_ID", self.DEFAULT_AUTH_CLIENT_ID
|
|
91
|
+
)
|
|
92
|
+
self.auth_realm_name = os.environ.get(
|
|
93
|
+
"CODEMIE_AUTH_REALM_NAME", self.DEFAULT_AUTH_REALM_NAME
|
|
94
|
+
)
|
|
95
|
+
self.auth_server_url = os.environ.get(
|
|
96
|
+
"CODEMIE_AUTH_SERVER_URL", self.DEFAULT_AUTH_SERVER_URL
|
|
97
|
+
)
|
|
98
|
+
self.api_domain = os.environ.get(
|
|
99
|
+
"CODEMIE_API_DOMAIN", self.DEFAULT_CODEMIE_API_DOMAIN
|
|
100
|
+
)
|
|
101
|
+
self.assistant_id = os.environ.get("CODEMIE_ASSISTANT_ID")
|
|
102
|
+
self.verify_ssl = os.environ.get("CODEMIE_VERIFY_SSL", "true").lower() == "true"
|
|
103
|
+
|
|
104
|
+
def __str__(self):
|
|
105
|
+
"""String representation of the settings (excluding sensitive data)."""
|
|
106
|
+
return (
|
|
107
|
+
f"Settings:\n"
|
|
108
|
+
f" Auth Client ID: {self.auth_client_id}\n"
|
|
109
|
+
f" Auth Realm Name: {self.auth_realm_name}\n"
|
|
110
|
+
f" Auth Server URL: {self.auth_server_url}\n"
|
|
111
|
+
f" API Domain: {self.api_domain}\n"
|
|
112
|
+
f" Assistant ID: {self.assistant_id}\n"
|
|
113
|
+
f" Verify SSL: {self.verify_ssl}\n"
|
|
114
|
+
)
|
src/server.py
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import uuid
|
|
3
|
+
from typing import Dict, List
|
|
4
|
+
|
|
5
|
+
from codemie_sdk import CodeMieClient
|
|
6
|
+
from codemie_sdk.models.assistant import (
|
|
7
|
+
AssistantChatRequest,
|
|
8
|
+
ChatMessage,
|
|
9
|
+
ChatRole,
|
|
10
|
+
)
|
|
11
|
+
from model import AssistantInfo, AssistantNotFoundError, Settings
|
|
12
|
+
|
|
13
|
+
from mcp.server.fastmcp import FastMCP
|
|
14
|
+
|
|
15
|
+
# Initialize settings at module level
|
|
16
|
+
try:
|
|
17
|
+
settings = Settings()
|
|
18
|
+
except Exception as e:
|
|
19
|
+
print(f"Error initializing settings: {str(e)}", file=sys.stderr)
|
|
20
|
+
sys.exit(1)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_client() -> CodeMieClient:
|
|
24
|
+
"""Gets authenticated CodeMie client instance."""
|
|
25
|
+
try:
|
|
26
|
+
client = CodeMieClient(
|
|
27
|
+
username=settings.auth.username,
|
|
28
|
+
password=settings.auth.password,
|
|
29
|
+
auth_client_id=settings.auth.client_id,
|
|
30
|
+
auth_client_secret=settings.auth.client_secret,
|
|
31
|
+
verify_ssl=settings.verify_ssl,
|
|
32
|
+
auth_realm_name=settings.auth_realm_name,
|
|
33
|
+
auth_server_url=settings.auth_server_url,
|
|
34
|
+
codemie_api_domain=settings.api_domain,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if not client.token:
|
|
38
|
+
print("Failed to obtain authentication token", file=sys.stderr)
|
|
39
|
+
raise ValueError("Failed to obtain authentication token")
|
|
40
|
+
|
|
41
|
+
print("Successfully initialized CodeMie client", file=sys.stdout)
|
|
42
|
+
return client
|
|
43
|
+
except Exception as e:
|
|
44
|
+
print(f"Error initializing client: {str(e)}", file=sys.stderr)
|
|
45
|
+
raise
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_assistant_info(assistant_id: str) -> AssistantInfo:
|
|
49
|
+
"""
|
|
50
|
+
Retrieve and validate assistant information.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
assistant_id: The ID of the assistant to retrieve
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
AssistantInfo: Validated assistant information
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
AssistantNotFoundError: If the assistant doesn't exist
|
|
60
|
+
ValueError: If there are validation errors
|
|
61
|
+
"""
|
|
62
|
+
try:
|
|
63
|
+
assistant = get_client().assistants.get(assistant_id)
|
|
64
|
+
if not assistant:
|
|
65
|
+
raise AssistantNotFoundError(assistant_id)
|
|
66
|
+
|
|
67
|
+
return AssistantInfo(
|
|
68
|
+
id=assistant.id,
|
|
69
|
+
name=assistant.name,
|
|
70
|
+
description=assistant.description,
|
|
71
|
+
project=assistant.project,
|
|
72
|
+
slug=assistant.slug,
|
|
73
|
+
)
|
|
74
|
+
except AssistantNotFoundError:
|
|
75
|
+
print(f"Assistant not found: {assistant_id}", file=sys.stderr)
|
|
76
|
+
raise
|
|
77
|
+
except Exception as e:
|
|
78
|
+
print(f"Error retrieving assistant {assistant_id}: {str(e)}", file=sys.stderr)
|
|
79
|
+
raise
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# Initialize FastMCP server
|
|
83
|
+
mcp = FastMCP("codemie-assistants")
|
|
84
|
+
# Get and validate assistant by identifier.
|
|
85
|
+
codemie_assistant = get_assistant_info(settings.assistant_id)
|
|
86
|
+
|
|
87
|
+
chat_tool_name = "ask_" + codemie_assistant.name.lower().replace(" ", "_")
|
|
88
|
+
chat_assistant_tool_description = (
|
|
89
|
+
f"""
|
|
90
|
+
This tool allows to ask and communicate with '{codemie_assistant.name}'.
|
|
91
|
+
Call this tool when '{codemie_assistant.slug}' or '{codemie_assistant.name} is present in message'
|
|
92
|
+
Purpose of '{codemie_assistant.name}' is: {codemie_assistant.description}.
|
|
93
|
+
You MUST always call this tool when '{codemie_assistant.name}' is tagged or referenced.
|
|
94
|
+
"""
|
|
95
|
+
+ """
|
|
96
|
+
Tool accepts the following parameters:
|
|
97
|
+
- message - required. User message to send to assistant
|
|
98
|
+
- conversation_id - string. Identifier of current conversation. if context or history contains Conversation_Id, this ID must be passed as parameter
|
|
99
|
+
must not be passed with the first call, only if returned previously
|
|
100
|
+
- history. List[Dict[str, str]]. MUST always be passed to assistant
|
|
101
|
+
Example of the given param [{"role": "User", "message": "show my tools"}, {"role": "Assistant", "message": "Here are the tools available for use: **functions.generic_jira_tool**}]
|
|
102
|
+
This parameter must always be filled when conversation is in progress
|
|
103
|
+
|
|
104
|
+
Returns assistant response. IMPORTANT: response must never be formatter or summarized.
|
|
105
|
+
"""
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@mcp.tool(
|
|
110
|
+
name=chat_tool_name,
|
|
111
|
+
description=chat_assistant_tool_description,
|
|
112
|
+
)
|
|
113
|
+
async def chat_with_assistant(
|
|
114
|
+
message: str,
|
|
115
|
+
history: List[Dict[str, str]] = None,
|
|
116
|
+
conversation_id: str = None,
|
|
117
|
+
) -> str:
|
|
118
|
+
"""
|
|
119
|
+
Chat with a specific assistant.
|
|
120
|
+
:param str message: required. User request to send to assistant
|
|
121
|
+
:param List[Dict[str, str]] history: Must be passed to assistant if user proceeds current conversation
|
|
122
|
+
:param str conversation_id: Identifier of current conversation
|
|
123
|
+
Example of the given param [{'role': 'User', 'message': 'show my tools'}, {'role': 'Assistant', 'message': 'Here are the tools available for use: **functions.generic_jira_tool**'}]
|
|
124
|
+
This parameter must always be filled when conversation is in progress
|
|
125
|
+
|
|
126
|
+
:return response from assistant.
|
|
127
|
+
"""
|
|
128
|
+
try:
|
|
129
|
+
client = get_client()
|
|
130
|
+
if not message or not message.strip():
|
|
131
|
+
print("Empty message provided", file=sys.stderr)
|
|
132
|
+
raise ValueError("Empty message provided")
|
|
133
|
+
|
|
134
|
+
# Convert history to ChatMessage objects if provided
|
|
135
|
+
chat_history = []
|
|
136
|
+
if history:
|
|
137
|
+
try:
|
|
138
|
+
for msg in history:
|
|
139
|
+
if (
|
|
140
|
+
not isinstance(msg, dict)
|
|
141
|
+
or "role" not in msg
|
|
142
|
+
or "message" not in msg
|
|
143
|
+
):
|
|
144
|
+
print(f"Invalid history format: {msg}", file=sys.stderr)
|
|
145
|
+
raise ValueError(f"Invalid history format: {msg}")
|
|
146
|
+
chat_history.append(
|
|
147
|
+
ChatMessage(role=ChatRole(msg["role"]), message=msg["message"])
|
|
148
|
+
)
|
|
149
|
+
except Exception as e:
|
|
150
|
+
print(f"Error processing chat history: {str(e)}", file=sys.stderr)
|
|
151
|
+
raise
|
|
152
|
+
|
|
153
|
+
# Create chat request
|
|
154
|
+
if not conversation_id:
|
|
155
|
+
conversation_id = str(uuid.uuid4())
|
|
156
|
+
print(f"Starting chat with assistant {settings.assistant_id} ", file=sys.stdout)
|
|
157
|
+
|
|
158
|
+
request = AssistantChatRequest(
|
|
159
|
+
text=message,
|
|
160
|
+
conversation_id=conversation_id,
|
|
161
|
+
history=chat_history,
|
|
162
|
+
stream=False,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Send chat request
|
|
166
|
+
response = client.assistants.chat(
|
|
167
|
+
assistant_id=settings.assistant_id, request=request
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if not response or not response.generated:
|
|
171
|
+
print(
|
|
172
|
+
f"Received empty response from {codemie_assistant.name}",
|
|
173
|
+
file=sys.stderr,
|
|
174
|
+
)
|
|
175
|
+
raise ValueError("Received empty response from assistant")
|
|
176
|
+
|
|
177
|
+
print(
|
|
178
|
+
f"Successfully received response from {codemie_assistant.name} for conversation {conversation_id}",
|
|
179
|
+
file=sys.stdout,
|
|
180
|
+
)
|
|
181
|
+
return f"Response: {response.generated}. Conversation_Id: {conversation_id}"
|
|
182
|
+
|
|
183
|
+
except Exception as e:
|
|
184
|
+
print(f"Error in chat_with_assistant: {str(e)}", file=sys.stderr)
|
|
185
|
+
raise
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
if __name__ == "__main__":
|
|
189
|
+
try:
|
|
190
|
+
print("Starting CodeMie Assistants MCP server", file=sys.stdout)
|
|
191
|
+
# Initialize and run the server
|
|
192
|
+
mcp.run(transport="stdio")
|
|
193
|
+
except Exception as e:
|
|
194
|
+
print(f"Error starting MCP server: {str(e)}", file=sys.stderr)
|
|
195
|
+
sys.exit(1)
|
assistants_mcp/__init__.py
DELETED
|
File without changes
|
assistants_mcp/assistants_mcp.py
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import uuid
|
|
3
|
-
from typing import Any, Dict, List, Optional
|
|
4
|
-
|
|
5
|
-
from codemie_sdk import CodeMieClient
|
|
6
|
-
from codemie_sdk.models.assistant import (
|
|
7
|
-
AssistantChatRequest,
|
|
8
|
-
ChatMessage,
|
|
9
|
-
ChatRole,
|
|
10
|
-
)
|
|
11
|
-
from mcp.server.fastmcp import FastMCP
|
|
12
|
-
|
|
13
|
-
DEFAULT_AUTH_CLIENT_ID = "codemie-sdk"
|
|
14
|
-
DEFAULT_AUTH_REALM_NAME = "codemie-prod"
|
|
15
|
-
DEFAULT_AUTH_SERVER_URL = (
|
|
16
|
-
"https://keycloak.eks-core.aws.main.edp.projects.epam.com/auth"
|
|
17
|
-
)
|
|
18
|
-
DEFAULT_CODEMIE_API_DOMAIN = "https://codemie.lab.epam.com/code-assistant-api"
|
|
19
|
-
|
|
20
|
-
# Initialize FastMCP server
|
|
21
|
-
mcp = FastMCP("codemie-assistants")
|
|
22
|
-
|
|
23
|
-
# Client instance
|
|
24
|
-
_client: Optional[CodeMieClient] = None
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def get_client() -> CodeMieClient:
|
|
28
|
-
"""Gets authenticated CodeMie client instance."""
|
|
29
|
-
username = os.getenv("CODEMIE_USERNAME")
|
|
30
|
-
password = os.getenv("CODEMIE_PASSWORD")
|
|
31
|
-
auth_client_id = os.getenv("CODEMIE_AUTH_CLIENT_ID", DEFAULT_AUTH_CLIENT_ID)
|
|
32
|
-
auth_realm_name = os.getenv("CODEMIE_AUTH_REALM_NAME", DEFAULT_AUTH_REALM_NAME)
|
|
33
|
-
auth_server_url = os.getenv("CODEMIE_AUTH_SERVER_URL", DEFAULT_AUTH_SERVER_URL)
|
|
34
|
-
codemie_api_domain = os.getenv("CODEMIE_API_DOMAIN", DEFAULT_CODEMIE_API_DOMAIN)
|
|
35
|
-
|
|
36
|
-
if not username or not password:
|
|
37
|
-
raise ValueError(
|
|
38
|
-
"Username and password must be set via environment variables: CODEMIE_USERNAME, CODEMIE_PASSWORD"
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
return CodeMieClient(
|
|
42
|
-
username=username,
|
|
43
|
-
password=password,
|
|
44
|
-
verify_ssl=False,
|
|
45
|
-
auth_client_id=auth_client_id,
|
|
46
|
-
auth_realm_name=auth_realm_name,
|
|
47
|
-
auth_server_url=auth_server_url,
|
|
48
|
-
codemie_api_domain=codemie_api_domain,
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
@mcp.tool()
|
|
53
|
-
async def get_tools() -> List[Dict[str, Any]]:
|
|
54
|
-
"""Get available tools for assistants."""
|
|
55
|
-
try:
|
|
56
|
-
print("Getting tools")
|
|
57
|
-
client = get_client()
|
|
58
|
-
if client.token is None:
|
|
59
|
-
raise ValueError("Client not initialized")
|
|
60
|
-
toolkits = client.assistants.get_tools()
|
|
61
|
-
|
|
62
|
-
# Convert to dict format for better visualization
|
|
63
|
-
tools_list = []
|
|
64
|
-
for toolkit in toolkits:
|
|
65
|
-
tools_list.extend(
|
|
66
|
-
[
|
|
67
|
-
{
|
|
68
|
-
"toolkit": toolkit.toolkit,
|
|
69
|
-
"tool": tool.name,
|
|
70
|
-
"label": tool.label or tool.name,
|
|
71
|
-
"settings_required": tool.settings_config,
|
|
72
|
-
}
|
|
73
|
-
for tool in toolkit.tools
|
|
74
|
-
]
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
return tools_list
|
|
78
|
-
except Exception as e:
|
|
79
|
-
print(f"Error getting tools: {str(e)}")
|
|
80
|
-
raise e
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
@mcp.tool()
|
|
84
|
-
async def get_assistants(
|
|
85
|
-
minimal: bool = True, project: Optional[str] = None
|
|
86
|
-
) -> List[Dict[str, Any]]:
|
|
87
|
-
"""Get list of available assistants.
|
|
88
|
-
|
|
89
|
-
Args:
|
|
90
|
-
minimal: Return minimal info (default: True)
|
|
91
|
-
project: Filter by project name
|
|
92
|
-
"""
|
|
93
|
-
client = get_client()
|
|
94
|
-
filters = {"project": project} if project else None
|
|
95
|
-
|
|
96
|
-
assistants = client.assistants.list(minimal_response=minimal, filters=filters)
|
|
97
|
-
|
|
98
|
-
# Convert to dict format
|
|
99
|
-
return [
|
|
100
|
-
{
|
|
101
|
-
"id": asst.id,
|
|
102
|
-
"name": asst.name,
|
|
103
|
-
"description": asst.description,
|
|
104
|
-
"project": getattr(asst, "project", None) if not minimal else None,
|
|
105
|
-
}
|
|
106
|
-
for asst in assistants
|
|
107
|
-
]
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
@mcp.tool()
|
|
111
|
-
async def chat_with_assistant(
|
|
112
|
-
message: str,
|
|
113
|
-
assistant_id: str,
|
|
114
|
-
conversation_id: Optional[str] = None,
|
|
115
|
-
history: Optional[List[Dict[str, str]]] = None,
|
|
116
|
-
) -> str:
|
|
117
|
-
"""Chat with a specific assistant.
|
|
118
|
-
|
|
119
|
-
Args:
|
|
120
|
-
message: User message
|
|
121
|
-
assistant_id: ID of the assistant to chat with
|
|
122
|
-
conversation_id: Optional conversation ID
|
|
123
|
-
history: Optional chat history as list of dicts with role and message
|
|
124
|
-
"""
|
|
125
|
-
client = get_client()
|
|
126
|
-
|
|
127
|
-
# Convert history to ChatMessage objects if provided
|
|
128
|
-
chat_history = []
|
|
129
|
-
if history:
|
|
130
|
-
for msg in history:
|
|
131
|
-
chat_history.append(
|
|
132
|
-
ChatMessage(role=ChatRole(msg["role"]), message=msg["message"])
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
# Create chat request
|
|
136
|
-
request = AssistantChatRequest(
|
|
137
|
-
text=message,
|
|
138
|
-
conversation_id=conversation_id if conversation_id else str(uuid.uuid4()),
|
|
139
|
-
history=chat_history,
|
|
140
|
-
stream=False, # For now using non-streaming responses
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
# Send chat request
|
|
144
|
-
response = client.assistants.chat(assistant_id=assistant_id, request=request)
|
|
145
|
-
|
|
146
|
-
return response.generated
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if __name__ == "__main__":
|
|
150
|
-
# Initialize and run the server
|
|
151
|
-
mcp.run(transport="stdio")
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: codemie-mcp-assistants
|
|
3
|
-
Version: 0.1.3
|
|
4
|
-
Summary: MCP server for connecting to CodeMie Assistants API
|
|
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 (==0.1.3)
|
|
12
|
-
Requires-Dist: httpx (>=0.27.0,<0.28.0)
|
|
13
|
-
Requires-Dist: mcp (>=1.2.0,<2.0.0)
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
assistants_mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
assistants_mcp/assistants_mcp.py,sha256=KQbbdrjZ5MVHH0pgMYxmW5t95kRtzL9I1CPz1eaJD0s,4513
|
|
3
|
-
codemie_mcp_assistants-0.1.3.dist-info/METADATA,sha256=EXJpO0CA-hNZ9Ec5R1NoQkKrjEEvdbDwNdG_beH9Prs,487
|
|
4
|
-
codemie_mcp_assistants-0.1.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
5
|
-
codemie_mcp_assistants-0.1.3.dist-info/entry_points.txt,sha256=sEfziMaK__okIZccdxljYgAdZVTsjCrrEE_qYQAMkC0,65
|
|
6
|
-
codemie_mcp_assistants-0.1.3.dist-info/RECORD,,
|
|
File without changes
|