fid-mcp 0.1.0__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 fid-mcp might be problematic. Click here for more details.

fid_mcp/__init__.py ADDED
@@ -0,0 +1 @@
1
+ # fid-mcp package
fid_mcp/config.py ADDED
@@ -0,0 +1,233 @@
1
+ from enum import Enum
2
+ from pydantic import BaseModel
3
+ from typing import Any, List, TypeVar, Type, cast, Callable
4
+ import json
5
+ import re
6
+ from pathlib import Path
7
+
8
+
9
+ T = TypeVar("T")
10
+ EnumT = TypeVar("EnumT", bound=Enum)
11
+
12
+
13
+ def from_str(x: Any) -> str:
14
+ assert isinstance(x, str)
15
+ return x
16
+
17
+
18
+ def from_int(x: Any) -> int:
19
+ assert isinstance(x, int) and not isinstance(x, bool)
20
+ return x
21
+
22
+
23
+ def to_enum(c: Type[EnumT], x: Any) -> EnumT:
24
+ assert isinstance(x, c)
25
+ return x.value
26
+
27
+
28
+ def to_class(c: Type[T], x: Any) -> dict:
29
+ assert isinstance(x, c)
30
+ return cast(Any, x).to_dict()
31
+
32
+
33
+ def from_list(f: Callable[[Any], T], x: Any) -> List[T]:
34
+ assert isinstance(x, list)
35
+ return [f(y) for y in x]
36
+
37
+
38
+ class Function(Enum):
39
+ """The type of function to execute"""
40
+
41
+ SHELL = "shell"
42
+
43
+
44
+ class ShellParams(BaseModel):
45
+ command: str
46
+ wait: int
47
+ """Wait time in seconds"""
48
+
49
+ @staticmethod
50
+ def from_dict(obj: Any) -> 'ShellParams':
51
+ assert isinstance(obj, dict)
52
+ command = from_str(obj.get("command"))
53
+ wait = from_int(obj.get("wait"))
54
+ return ShellParams(command=command, wait=wait)
55
+
56
+ def to_dict(self) -> dict:
57
+ result: dict = {}
58
+ result["command"] = from_str(self.command)
59
+ result["wait"] = from_int(self.wait)
60
+ return result
61
+
62
+
63
+ class Step(BaseModel):
64
+ function: Function
65
+ """The type of function to execute"""
66
+
67
+ shell_params: ShellParams
68
+
69
+ @staticmethod
70
+ def from_dict(obj: Any) -> 'Step':
71
+ assert isinstance(obj, dict)
72
+ function = Function(obj.get("function"))
73
+ shell_params = ShellParams.from_dict(obj.get("shellParams"))
74
+ return Step(function=function, shell_params=shell_params)
75
+
76
+ def to_dict(self) -> dict:
77
+ result: dict = {}
78
+ result["function"] = to_enum(Function, self.function)
79
+ result["shellParams"] = to_class(ShellParams, self.shell_params)
80
+ return result
81
+
82
+
83
+ class Param(BaseModel):
84
+ default: str
85
+ name: str
86
+
87
+ @staticmethod
88
+ def from_dict(obj: Any) -> 'Param':
89
+ assert isinstance(obj, dict)
90
+ default = from_str(obj.get("default"))
91
+ name = from_str(obj.get("name"))
92
+ return Param(default=default, name=name)
93
+
94
+ def to_dict(self) -> dict:
95
+ result: dict = {}
96
+ result["default"] = from_str(self.default)
97
+ result["name"] = from_str(self.name)
98
+ return result
99
+
100
+
101
+ class Tool(BaseModel):
102
+ description: str
103
+ name: str
104
+ steps: List[Step]
105
+ tool_params: List[Param]
106
+
107
+ @staticmethod
108
+ def from_dict(obj: Any) -> 'Tool':
109
+ assert isinstance(obj, dict)
110
+ description = from_str(obj.get("description"))
111
+ name = from_str(obj.get("name"))
112
+ steps = from_list(Step.from_dict, obj.get("steps"))
113
+ tool_params = from_list(Param.from_dict, obj.get("toolParams"))
114
+ return Tool(description=description, name=name, steps=steps, tool_params=tool_params)
115
+
116
+ def to_dict(self) -> dict:
117
+ result: dict = {}
118
+ result["description"] = from_str(self.description)
119
+ result["name"] = from_str(self.name)
120
+ result["steps"] = from_list(lambda x: to_class(Step, x), self.steps)
121
+ result["toolParams"] = from_list(lambda x: to_class(Param, x), self.tool_params)
122
+ return result
123
+
124
+
125
+ class Version(Enum):
126
+ THE_100 = "1.0.0"
127
+
128
+
129
+ class Coordinate(BaseModel):
130
+ description: str
131
+ name: str
132
+ tools: List[Tool]
133
+ version: Version
134
+
135
+ @staticmethod
136
+ def from_dict(obj: Any) -> 'Coordinate':
137
+ assert isinstance(obj, dict)
138
+ description = from_str(obj.get("description"))
139
+ name = from_str(obj.get("name"))
140
+ tools = from_list(Tool.from_dict, obj.get("tools"))
141
+ version = Version(obj.get("version"))
142
+ return Coordinate(description=description, name=name, tools=tools, version=version)
143
+
144
+ def to_dict(self) -> dict:
145
+ result: dict = {}
146
+ result["description"] = from_str(self.description)
147
+ result["name"] = from_str(self.name)
148
+ result["tools"] = from_list(lambda x: to_class(Tool, x), self.tools)
149
+ result["version"] = to_enum(Version, self.version)
150
+ return result
151
+
152
+
153
+ def coordinate_from_dict(s: Any) -> Coordinate:
154
+ return Coordinate.from_dict(s)
155
+
156
+
157
+ def coordinate_to_dict(x: Coordinate) -> Any:
158
+ return to_class(Coordinate, x)
159
+
160
+
161
+ def load_and_validate_config(config_path: str) -> Coordinate:
162
+ """Load and validate a fidtools configuration file using Pydantic models"""
163
+ config_path = Path(config_path)
164
+
165
+ # Load the configuration file
166
+ with open(config_path, 'r') as f:
167
+ config_data = json.load(f)
168
+
169
+ # Validate using Pydantic models and additional validation
170
+ validate_config_dict(config_data)
171
+
172
+ # Convert to Coordinate object
173
+ return coordinate_from_dict(config_data)
174
+
175
+
176
+ def validate_config_dict(config_data: dict) -> None:
177
+ """Validate a configuration dictionary using Pydantic models and check parameter references"""
178
+ # Validate using Pydantic by attempting to create a Coordinate object
179
+ try:
180
+ coordinate_from_dict(config_data)
181
+ except Exception as e:
182
+ raise ValueError(f"Configuration validation failed: {str(e)}")
183
+
184
+ # Additional validation: check parameter references
185
+ _validate_parameter_references(config_data)
186
+
187
+
188
+ def _validate_parameter_references(config_data: dict) -> None:
189
+ """Validate that all parameter references in shellParams are defined in toolParams"""
190
+ for tool_idx, tool in enumerate(config_data.get("tools", [])):
191
+ tool_name = tool.get("name", f"tool[{tool_idx}]")
192
+
193
+ # Get defined parameters
194
+ defined_params = set()
195
+ for param in tool.get("toolParams", []):
196
+ defined_params.add(param.get("name"))
197
+
198
+ # Check each step for parameter references
199
+ for step_idx, step in enumerate(tool.get("steps", [])):
200
+ if step.get("function") == "shell" and "shellParams" in step:
201
+ shell_params = step["shellParams"]
202
+
203
+ # Check parameter references in command
204
+ if "command" in shell_params:
205
+ referenced_params = _extract_parameter_references(shell_params["command"])
206
+
207
+ # Check if all referenced parameters are defined
208
+ undefined_params = referenced_params - defined_params
209
+ if undefined_params:
210
+ raise ValueError(
211
+ f"Tool '{tool_name}' step {step_idx}: "
212
+ f"shellParams.command references undefined parameters: {sorted(undefined_params)}. "
213
+ f"Defined parameters: {sorted(defined_params)}"
214
+ )
215
+
216
+
217
+ def _extract_parameter_references(text: str) -> set:
218
+ """Extract parameter references (${param}) from a text string"""
219
+ if not isinstance(text, str):
220
+ return set()
221
+
222
+ # Find all ${...} patterns
223
+ pattern = r'\$\{([^}]+)\}'
224
+ matches = re.findall(pattern, text)
225
+
226
+ # Extract simple parameter names (not complex paths like params.name or step[0].data)
227
+ simple_params = set()
228
+ for match in matches:
229
+ # Only consider simple parameter names (no dots, no brackets)
230
+ if '.' not in match and '[' not in match and ']' not in match:
231
+ simple_params.add(match)
232
+
233
+ return simple_params