fid-mcp 0.1.0__py3-none-any.whl → 0.1.2__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/server.py +128 -10
- {fid_mcp-0.1.0.dist-info → fid_mcp-0.1.2.dist-info}/METADATA +5 -4
- fid_mcp-0.1.2.dist-info/RECORD +9 -0
- fid_mcp-0.1.0.dist-info/RECORD +0 -9
- {fid_mcp-0.1.0.dist-info → fid_mcp-0.1.2.dist-info}/WHEEL +0 -0
- {fid_mcp-0.1.0.dist-info → fid_mcp-0.1.2.dist-info}/entry_points.txt +0 -0
- {fid_mcp-0.1.0.dist-info → fid_mcp-0.1.2.dist-info}/licenses/LICENSE +0 -0
fid_mcp/server.py
CHANGED
|
@@ -204,14 +204,38 @@ class DynamicToolServer:
|
|
|
204
204
|
}
|
|
205
205
|
self.tools: Dict[str, Dict[str, Any]] = {}
|
|
206
206
|
|
|
207
|
+
# Fid knowledge base configuration
|
|
208
|
+
self.fid_config = None
|
|
209
|
+
self.fid_pat = os.environ.get("FID_PAT")
|
|
210
|
+
|
|
207
211
|
# Setup handlers
|
|
208
212
|
self.setup_handlers()
|
|
209
213
|
|
|
210
214
|
def setup_handlers(self):
|
|
211
215
|
@self.server.list_tools()
|
|
212
216
|
async def handle_list_tools() -> list[types.Tool]:
|
|
213
|
-
"""Return all dynamically loaded tools"""
|
|
217
|
+
"""Return all dynamically loaded tools plus Fid search"""
|
|
214
218
|
tools = []
|
|
219
|
+
|
|
220
|
+
# Always add Fid search tool (will show error if not configured when called)
|
|
221
|
+
tools.append(
|
|
222
|
+
types.Tool(
|
|
223
|
+
name="search",
|
|
224
|
+
description="Search the user's Fid knowledge base, which typically contains datasheets, integration manuals, API references, and other technical documentation, particularly for hardware components. ALWAYS try searching Fid first if the user asks about a hardware component, datasheet, manual, or API spec.",
|
|
225
|
+
inputSchema={
|
|
226
|
+
"type": "object",
|
|
227
|
+
"properties": {
|
|
228
|
+
"query": {
|
|
229
|
+
"type": "string",
|
|
230
|
+
"description": "The search query string",
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
"required": ["query"],
|
|
234
|
+
},
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Add dynamically loaded tools
|
|
215
239
|
for tool_name, tool_def in self.tools.items():
|
|
216
240
|
# Build input schema from parameters
|
|
217
241
|
properties = {}
|
|
@@ -249,7 +273,33 @@ class DynamicToolServer:
|
|
|
249
273
|
async def handle_call_tool(
|
|
250
274
|
name: str, arguments: Dict[str, Any] | None
|
|
251
275
|
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
|
|
252
|
-
"""Execute a dynamically loaded tool"""
|
|
276
|
+
"""Execute a dynamically loaded tool or Fid search"""
|
|
277
|
+
|
|
278
|
+
# Handle Fid search tool
|
|
279
|
+
if name == "search":
|
|
280
|
+
if not self.fid_pat:
|
|
281
|
+
error_msg = "Personal Access Token (PAT) missing. Please install the Fid MCP server according to these instructions: https://docs.fidlabs.ai/en/latest/connecting-agents.html"
|
|
282
|
+
return [types.TextContent(type="text", text=f"Error: {error_msg}")]
|
|
283
|
+
|
|
284
|
+
if not self.fid_config:
|
|
285
|
+
error_msg = "No Fid toolkit found in current directory. Download an existing toolkit from your Fid project, or create a new one with these instructions: https://docs.fidlabs.ai/en/latest/toolkits.html"
|
|
286
|
+
return [types.TextContent(type="text", text=f"Error: {error_msg}")]
|
|
287
|
+
|
|
288
|
+
if not arguments or "query" not in arguments:
|
|
289
|
+
raise ValueError("Query parameter is required")
|
|
290
|
+
|
|
291
|
+
try:
|
|
292
|
+
results = await self._search_fid_knowledge(arguments["query"])
|
|
293
|
+
return [
|
|
294
|
+
types.TextContent(
|
|
295
|
+
type="text", text=json.dumps(results, indent=2)
|
|
296
|
+
)
|
|
297
|
+
]
|
|
298
|
+
except Exception as e:
|
|
299
|
+
error_msg = f"Search failed: {str(e)}"
|
|
300
|
+
return [types.TextContent(type="text", text=f"Error: {error_msg}")]
|
|
301
|
+
|
|
302
|
+
# Handle dynamic tools
|
|
253
303
|
if name not in self.tools:
|
|
254
304
|
raise ValueError(f"Unknown tool: {name}")
|
|
255
305
|
|
|
@@ -324,8 +374,57 @@ class DynamicToolServer:
|
|
|
324
374
|
output = self._build_output(tool_def.get("output"), exec_context)
|
|
325
375
|
return [types.TextContent(type="text", text=json.dumps(output, indent=2))]
|
|
326
376
|
|
|
377
|
+
async def _search_fid_knowledge(self, query: str) -> Dict[str, Any]:
|
|
378
|
+
"""Search the Fid knowledge base"""
|
|
379
|
+
import aiohttp
|
|
380
|
+
import time
|
|
381
|
+
|
|
382
|
+
start_time = time.time()
|
|
383
|
+
default_k = 6
|
|
384
|
+
|
|
385
|
+
# Use the correct projects/{projectId}/search endpoint
|
|
386
|
+
url = f"{self.fid_config['apiBaseUrl']}/projects/{self.fid_config['projectId']}/search"
|
|
387
|
+
headers = {
|
|
388
|
+
"X-API-Key": self.fid_pat,
|
|
389
|
+
"X-Client-Source": "mcp_client",
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
payload = {
|
|
393
|
+
"query": query,
|
|
394
|
+
"k": default_k,
|
|
395
|
+
"userId": "", # Required field, can be empty
|
|
396
|
+
"search_method": "multi_stage",
|
|
397
|
+
"multi_stage_methods": ["full_text", "vector"],
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
async with aiohttp.ClientSession() as session:
|
|
401
|
+
async with session.post(url, json=payload, headers=headers) as response:
|
|
402
|
+
if response.status != 200:
|
|
403
|
+
error_text = await response.text()
|
|
404
|
+
raise ValueError(
|
|
405
|
+
f"API request failed with status {response.status}: {error_text}"
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
data = await response.json()
|
|
409
|
+
|
|
410
|
+
# Log search time
|
|
411
|
+
duration = time.time() - start_time
|
|
412
|
+
|
|
413
|
+
if not data.get("results"):
|
|
414
|
+
return {
|
|
415
|
+
"results": [],
|
|
416
|
+
"message": "No search results found",
|
|
417
|
+
"duration": duration,
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return {
|
|
421
|
+
"results": data.get("results", []),
|
|
422
|
+
"totalCount": data.get("totalCount"),
|
|
423
|
+
"duration": duration,
|
|
424
|
+
}
|
|
425
|
+
|
|
327
426
|
def load_toolset(self, filepath: Path):
|
|
328
|
-
"""Load and register tools from a .fidtools file"""
|
|
427
|
+
"""Load and register tools from a .fidtools or fidtools.json file"""
|
|
329
428
|
with open(filepath, "r") as f:
|
|
330
429
|
toolset = json.load(f)
|
|
331
430
|
|
|
@@ -335,6 +434,13 @@ class DynamicToolServer:
|
|
|
335
434
|
except ValueError as e:
|
|
336
435
|
raise ValueError(f"Failed to validate toolset '{filepath}': {e}")
|
|
337
436
|
|
|
437
|
+
# Extract Fid configuration if present
|
|
438
|
+
if "projectId" in toolset and "apiBaseUrl" in toolset:
|
|
439
|
+
self.fid_config = {
|
|
440
|
+
"projectId": toolset["projectId"],
|
|
441
|
+
"apiBaseUrl": toolset["apiBaseUrl"],
|
|
442
|
+
}
|
|
443
|
+
|
|
338
444
|
# Register each tool
|
|
339
445
|
for tool_def in toolset["tools"]:
|
|
340
446
|
self.tools[tool_def["name"]] = tool_def
|
|
@@ -425,17 +531,29 @@ class DynamicToolServer:
|
|
|
425
531
|
f.write(f"os.getcwd(): {os.getcwd()}\n")
|
|
426
532
|
f.write(f"Using working_dir: {working_dir}\n")
|
|
427
533
|
|
|
428
|
-
# Look for .fidtools file in actual working directory
|
|
429
|
-
|
|
534
|
+
# Look for .fidtools or fidtools.json file in actual working directory
|
|
535
|
+
config_files = [working_dir / ".fidtools", working_dir / "fidtools.json"]
|
|
536
|
+
|
|
537
|
+
config_file = None
|
|
538
|
+
for file_path in config_files:
|
|
539
|
+
if file_path.exists() and file_path.is_file():
|
|
540
|
+
config_file = file_path
|
|
541
|
+
break
|
|
542
|
+
|
|
430
543
|
with open(debug_log, "a") as f:
|
|
431
|
-
f.write(
|
|
432
|
-
|
|
544
|
+
f.write(
|
|
545
|
+
f"Looking for config files: {', '.join(str(f) for f in config_files)}\n"
|
|
546
|
+
)
|
|
547
|
+
if config_file:
|
|
548
|
+
f.write(f"Found config file: {config_file}\n")
|
|
549
|
+
else:
|
|
550
|
+
f.write("No config file found\n")
|
|
433
551
|
|
|
434
|
-
if
|
|
552
|
+
if config_file:
|
|
435
553
|
try:
|
|
436
|
-
self.load_toolset(
|
|
554
|
+
self.load_toolset(config_file)
|
|
437
555
|
with open(debug_log, "a") as f:
|
|
438
|
-
f.write(f"Successfully loaded toolset from {
|
|
556
|
+
f.write(f"Successfully loaded toolset from {config_file}\n")
|
|
439
557
|
f.write(f"Loaded {len(self.tools)} tools\n")
|
|
440
558
|
except Exception as e:
|
|
441
559
|
with open(debug_log, "a") as f:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fid-mcp
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: MCP Server for serving configurable Fid toolkits
|
|
5
5
|
Project-URL: Homepage, https://fidlabs.ai
|
|
6
6
|
Author-email: Fid Labs <contact@fidlabs.ai>
|
|
@@ -10,6 +10,7 @@ Keywords: mcp,model-context-protocol,server,tools
|
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Requires-Python: >=3.8
|
|
13
|
+
Requires-Dist: aiohttp
|
|
13
14
|
Requires-Dist: mcp
|
|
14
15
|
Requires-Dist: pexpect
|
|
15
16
|
Requires-Dist: pydantic
|
|
@@ -17,12 +18,12 @@ Description-Content-Type: text/markdown
|
|
|
17
18
|
|
|
18
19
|
# Fid MCP Server
|
|
19
20
|
|
|
20
|
-
MCP server that dynamically serves tools according to `.fidtools` configs present in the working directory.
|
|
21
|
+
MCP server that dynamically serves tools according to `fidtools.json` or `.fidtools` configs present in the working directory. Toolkits are generated by the Fid web application and intended to allow developers to quickly onboard coding agents to their unique development environments.
|
|
21
22
|
|
|
22
23
|
To install:
|
|
23
24
|
```bash
|
|
24
|
-
claude mcp add
|
|
25
|
+
claude mcp add Fid --scope user --env FID_PAT=YOUR_FID_PAT -- uvx fid-mcp
|
|
25
26
|
```
|
|
26
27
|
Note: retrieve your PAT from the user menu in your Fid project, or just use the pre-constructed install command provided by the "Connect to your Agent" button.
|
|
27
28
|
|
|
28
|
-
See [fidlabs.ai](https://fidlabs.ai) for more information or reach out to us at [contact@fidlabs.ai](mailto:contact@fidlabs.ai).
|
|
29
|
+
See [docs.fidlabs.ai](https://docs.fidlabs.ai) for more information or reach out to us at [contact@fidlabs.ai](mailto:contact@fidlabs.ai).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
fid_mcp/__init__.py,sha256=cv_5UWE6kUBeTfAY6JZq9o47hEfzaLhcoKW0rw8GKq8,17
|
|
2
|
+
fid_mcp/config.py,sha256=YEH9BD4UWU9Hp3ifc3PGg4nk3FvFadfRt52-_fhCaqk,7365
|
|
3
|
+
fid_mcp/server.py,sha256=LctNUIjsBzvza6-NGNOxcByHjyFG83ojfxUdoELX8ak,22501
|
|
4
|
+
fid_mcp/shell.py,sha256=mLbGIVuk4LGCT37tS8deDOyTSY2zPG57WE9KBGsKSHg,26459
|
|
5
|
+
fid_mcp-0.1.2.dist-info/METADATA,sha256=VCqD3CDKW5tUkwe-HCtQGLFKFn_trrjxL0m4hBiArrM,1259
|
|
6
|
+
fid_mcp-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
+
fid_mcp-0.1.2.dist-info/entry_points.txt,sha256=Xdh2RDDC7r5rIXe1kM3smsA1rgNxd1tafL_JuuTJf2o,48
|
|
8
|
+
fid_mcp-0.1.2.dist-info/licenses/LICENSE,sha256=5SkiXC7s2RbMDCdmq4N6MCWNKw151sFmkGbWS9BLSJE,177
|
|
9
|
+
fid_mcp-0.1.2.dist-info/RECORD,,
|
fid_mcp-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
fid_mcp/__init__.py,sha256=cv_5UWE6kUBeTfAY6JZq9o47hEfzaLhcoKW0rw8GKq8,17
|
|
2
|
-
fid_mcp/config.py,sha256=YEH9BD4UWU9Hp3ifc3PGg4nk3FvFadfRt52-_fhCaqk,7365
|
|
3
|
-
fid_mcp/server.py,sha256=c3RYpLvUKJcMC4W26YfQ2lBWQwWRNOODESUsn-UDH6s,17633
|
|
4
|
-
fid_mcp/shell.py,sha256=mLbGIVuk4LGCT37tS8deDOyTSY2zPG57WE9KBGsKSHg,26459
|
|
5
|
-
fid_mcp-0.1.0.dist-info/METADATA,sha256=QaenjwxRoRNpK46yGi-S9UdSex05q-Ma9ftD0m52DEs,1210
|
|
6
|
-
fid_mcp-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
-
fid_mcp-0.1.0.dist-info/entry_points.txt,sha256=Xdh2RDDC7r5rIXe1kM3smsA1rgNxd1tafL_JuuTJf2o,48
|
|
8
|
-
fid_mcp-0.1.0.dist-info/licenses/LICENSE,sha256=5SkiXC7s2RbMDCdmq4N6MCWNKw151sFmkGbWS9BLSJE,177
|
|
9
|
-
fid_mcp-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|