Skip to content

Commit 16ac912

Browse files
committed
updated mcp tools
1 parent fac009e commit 16ac912

4 files changed

Lines changed: 1377 additions & 234 deletions

File tree

Lines changed: 52 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
# Fit BEPS Dataset Workflow
22

3-
This is a small-model-friendly workflow for the common task:
3+
This is the small-model-friendly workflow for the common BEPS fitting task.
44

5-
1. Read a BEPS file with `SciFiReaders`
6-
2. Fit a loop slice with `fit_beps_loops_tool`
7-
3. Fit an SHO slice with `fit_sho_response_tool`
8-
4. Save both parameter maps as `sidpy.Dataset` objects
5+
## Workflow
96

10-
The workflow name published by the MCP server is:
7+
1. Read the file with the SciFiReaders MCP server.
8+
2. Pass the original file path to the sidpy workflow tool.
9+
3. Fit SHO over the full DC sweep for one cycle.
10+
4. Derive the loop input by projecting the fitted SHO amplitude and phase
11+
with BGlib `projectLoop`.
12+
5. Save the SHO and BEPS fit-parameter datasets.
13+
14+
The published workflow name is:
1115

1216
`analysis.fit_beps_dataset`
1317

@@ -17,125 +21,73 @@ Use these as the starting placeholders:
1721

1822
- `file_path`: `/path/to/PTO_5x5.h5`
1923
- `channel_name`: `Channel_000`
20-
- `beps_frequency_index`: `23`
21-
- `beps_cycle_index`: `0`
22-
- `sho_dc_index`: `49`
2324
- `sho_cycle_index`: `1`
24-
- `use_kmeans`: `true`
25+
- `use_kmeans`: `false`
2526
- `n_clusters`: `4`
27+
- `scifireaders_return_mode`: `file`
28+
- `sho_dataset_name`: `sho_fit_parameters`
29+
- `beps_dataset_name`: `beps_fit_parameters`
2630

27-
## Workflow Shape
28-
29-
### 1. Read the file
30-
31-
Use `SciFiReaders.NSIDReader(file_path)` and read the channel named `channel_name`.
32-
33-
Expected result:
34-
35-
- a `sidpy.Dataset`
36-
- shape similar to `(x, y, frequency, dc_offset, cycle)`
37-
38-
### 2. Build the BEPS slice
39-
40-
Take the real-valued loop slice:
31+
## Tool Call Sequence
4132

42-
```python
43-
beps_data = data[:, :, beps_frequency_index, :, beps_cycle_index].real
44-
dc_voltage = data._axes[3].values
45-
```
33+
### 1. Read the file with SciFiReaders MCP
4634

47-
Then call:
35+
Call:
4836

4937
```python
50-
fit_beps_loops_tool(
51-
data=beps_data,
52-
dc_voltage=dc_voltage,
53-
use_kmeans=use_kmeans,
54-
n_clusters=n_clusters,
55-
return_cov=False,
56-
loss="linear",
57-
dataset_name="beps_loop_fit",
38+
read_scifireaders_file(
39+
file_path=file_path,
40+
return_mode="file",
5841
)
5942
```
6043

61-
Expected output:
62-
63-
- `fit_kind = "beps_loop"`
64-
- `parameter_shape = [x, y, 9]`
65-
- `parameter_labels = LOOP_PARAMETER_LABELS`
44+
This returns a small result object with an `output_file_path` field. Pass that
45+
path if you want, but the sidpy workflow tool only needs the original
46+
`source_file_path`.
6647

67-
### 3. Save the BEPS fit parameters
48+
### 2. Run the BEPS workflow in sidpy
6849

69-
Call `create_dataset_tool` with the BEPS parameter array.
70-
71-
Use:
72-
73-
- `quantity = "fit_parameter"`
74-
- `units = "a.u."`
75-
- spatial dimensions for `X` and `Y`
76-
- spectral dimension `fit_parameter` with 9 entries
77-
78-
Attach metadata such as:
79-
80-
- `fit_kind`
81-
- `source_dataset`
82-
- `source_slice`
83-
84-
### 4. Build the SHO slice
85-
86-
Take the complex frequency slice:
50+
Call:
8751

8852
```python
89-
sho_data = data[:, :, :, sho_dc_index, sho_cycle_index]
90-
frequency = data._axes[2].values
91-
```
92-
93-
Then call:
94-
95-
```python
96-
fit_sho_response_tool(
97-
real_data=sho_data.real,
98-
imag_data=sho_data.imag,
99-
frequency=frequency,
53+
fit_beps_dataset_workflow_tool(
54+
source_file_path=file_path,
55+
channel_name=channel_name,
56+
dataset_name=file_path,
57+
cycle_index=sho_cycle_index,
10058
use_kmeans=use_kmeans,
10159
n_clusters=n_clusters,
10260
return_cov=False,
10361
loss="linear",
104-
dataset_name="sho_response_fit",
62+
sho_dataset_name=sho_dataset_name,
63+
beps_dataset_name=beps_dataset_name,
10564
)
10665
```
10766

108-
Expected output:
109-
110-
- `fit_kind = "sho"`
111-
- `parameter_shape = [x, y, 4]`
112-
- `parameter_labels = SHO_PARAMETER_LABELS`
113-
114-
### 5. Save the SHO fit parameters
115-
116-
Call `create_dataset_tool` again with the SHO parameter array.
67+
This one sidpy tool performs the full sequence internally:
11768

118-
Use:
69+
1. Register the source file as a sidpy dataset.
70+
2. Fit SHO over the full DC sweep for the selected cycle.
71+
3. Save the SHO fit-parameter map as a `sidpy.Dataset` with a DC axis.
72+
4. Project the fitted SHO amplitude and phase into a piezoresponse loop with
73+
BGlib `projectLoop`.
74+
5. Fit the BEPS loop slice from that projected piezoresponse.
75+
6. Save the BEPS fit-parameter map as a `sidpy.Dataset`.
11976

120-
- `quantity = "fit_parameter"`
121-
- `units = "a.u."`
122-
- spatial dimensions for `X` and `Y`
123-
- spectral dimension `fit_parameter` with 4 entries
77+
## Expected Result
12478

125-
Attach metadata such as:
79+
The tool returns:
12680

127-
- `fit_kind`
128-
- `source_dataset`
129-
- `source_slice`
81+
- `source_dataset_id`
82+
- `sho_dataset_id`
83+
- `beps_dataset_id`
84+
- `sho_dataset` with `fit_quality`
85+
- `beps_dataset` with `fit_quality`
13086

13187
## Practical Notes
13288

133-
- Keep the number of tool calls small and linear.
134-
- Read once, slice once, fit once, save once.
135-
- If the model gets confused, prefer this exact order:
136-
1. read
137-
2. fit BEPS
138-
3. save BEPS
139-
4. fit SHO
140-
5. save SHO
141-
- The saved fit-parameter datasets should preserve the `X` and `Y` axes from the source file.
89+
- Keep the workflow in this exact order: optionally read with SciFiReaders, then one sidpy workflow call.
90+
- The SHO metadata stores the loop slice indices used by the derive step.
91+
- The SHO metadata stores the selected cycle index used by the derive step.
92+
- The loop fit uses the projected piezoresponse, not the raw loop waveform.
93+
- The saved fit datasets preserve the `X` and `Y` axes from the source file.

examples/mcp/run_beps_mcp_client.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ class JsonRpcMessage:
6565
payload: Dict[str, Any]
6666

6767

68+
def _json_default(value: Any) -> Any:
69+
"""Encode values that the standard JSON encoder cannot handle."""
70+
if isinstance(value, complex):
71+
return {"__complex__": [value.real, value.imag]}
72+
raise TypeError(f"Object of type {value.__class__.__name__} is not JSON serializable")
73+
74+
6875
class StdioMcpClient:
6976
"""Small MCP client that speaks JSON-RPC over stdio."""
7077

@@ -173,7 +180,7 @@ def request(self, method: str, params: Optional[Dict[str, Any]] = None) -> Dict[
173180
def _send(self, payload: Dict[str, Any]) -> None:
174181
if self.proc is None or self.proc.stdin is None:
175182
raise McpError("MCP server is not running.")
176-
body = (json.dumps(payload) + "\n").encode("utf-8")
183+
body = (json.dumps(payload, default=_json_default) + "\n").encode("utf-8")
177184
self._debug(f"send: {payload.get('method', 'response')}")
178185
self.proc.stdin.write(body)
179186
self.proc.stdin.flush()

0 commit comments

Comments
 (0)