From c6e2025b880eae1157a1e71980137027e82d338f Mon Sep 17 00:00:00 2001 From: Leopold Talirz Date: Fri, 29 May 2026 14:28:47 +0200 Subject: [PATCH 1/2] feat: add jobs//settings endpoint So far, the settings of a particular job were not exposed via the API. This made it impossible to do something like "run the same job as but change settings a,b,c". --- amorphouspy_api/src/amorphouspy_api/models.py | 10 ++++++ .../src/amorphouspy_api/routers/jobs.py | 19 ++++++++++ amorphouspy_api/src/tests/test_jobs.py | 36 +++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/amorphouspy_api/src/amorphouspy_api/models.py b/amorphouspy_api/src/amorphouspy_api/models.py index 54fcf190..c14f10f8 100644 --- a/amorphouspy_api/src/amorphouspy_api/models.py +++ b/amorphouspy_api/src/amorphouspy_api/models.py @@ -487,6 +487,16 @@ class JobStatusResponse(BaseModel): ) +class JobSettingsResponse(BaseModel): + """Response for ``GET /jobs/{id}/settings``.""" + + job_id: str + settings: dict = Field( + ..., + description="Original submission parameters (composition, potential, simulation, analyses, electrostatics, tags).", + ) + + class JobResultsResponse(BaseModel): """Response for ``GET /jobs/{id}/results``.""" diff --git a/amorphouspy_api/src/amorphouspy_api/routers/jobs.py b/amorphouspy_api/src/amorphouspy_api/routers/jobs.py index a0c3a451..5a907e8c 100644 --- a/amorphouspy_api/src/amorphouspy_api/routers/jobs.py +++ b/amorphouspy_api/src/amorphouspy_api/routers/jobs.py @@ -12,6 +12,7 @@ POST /jobs:search - search jobs across all statuses GET /jobs/{id} - poll job status POST /jobs/{id}:cancel - cancel a running job + GET /jobs/{id}/settings - original submission settings GET /jobs/{id}/results - all analysis results GET /jobs/{id}/results/{analysis} - single analysis result GET /jobs/{id}/structure - export quenched structure @@ -40,6 +41,7 @@ JobSearchMatch, JobSearchRequest, JobSearchResponse, + JobSettingsResponse, JobStatus, JobStatusResponse, JobSubmission, @@ -379,6 +381,23 @@ def update_tags(job_id: str, body: TagsUpdate) -> TagsResponse: return TagsResponse(job_id=job_id, tags=sorted(set(body.tags))) +@router.get("/{job_id}/settings", response_model=JobSettingsResponse) +def get_job_settings(job_id: str) -> JobSettingsResponse: + """Return the original submission settings for a job.""" + store = get_job_store() + job = store.get_job(job_id) + if not job: + raise HTTPException(status_code=404, detail="Job not found") + + if not job.request_data: + raise HTTPException(status_code=404, detail="No settings stored for this job") + + return JobSettingsResponse( + job_id=job.job_id, + settings=job.request_data, + ) + + @router.get("/{job_id}/results", response_model=JobResultsResponse) def get_job_results(job_id: str) -> JobResultsResponse: """All completed analysis results for a job.""" diff --git a/amorphouspy_api/src/tests/test_jobs.py b/amorphouspy_api/src/tests/test_jobs.py index d1c6c7b5..c6b0fbef 100644 --- a/amorphouspy_api/src/tests/test_jobs.py +++ b/amorphouspy_api/src/tests/test_jobs.py @@ -309,6 +309,42 @@ def test_get_results_no_data() -> None: assert resp.status_code == 404 +# --------------------------------------------------------------------------- +# GET /jobs/{id}/settings +# --------------------------------------------------------------------------- + + +def test_get_settings_completed() -> None: + _insert_completed_job("j-settings-1") + + resp = client.get("/jobs/j-settings-1/settings") + assert resp.status_code == 200 + data = resp.json() + assert data["job_id"] == "j-settings-1" + assert "composition" in data["settings"] + assert "potential" in data["settings"] + + +def test_get_settings_not_found() -> None: + resp = client.get("/jobs/nonexistent/settings") + assert resp.status_code == 404 + + +def test_get_settings_no_request_data() -> None: + store = get_job_store() + store.create_job( + Job( + job_id="j-no-settings", + request_hash="nosettings", + composition="SiO2 100", + potential="pmmcs", + status="completed", + ) + ) + resp = client.get("/jobs/j-no-settings/settings") + assert resp.status_code == 404 + + # --------------------------------------------------------------------------- # GET /jobs/{id}/results/{analysis} # --------------------------------------------------------------------------- From 4b0a66e2303258b1bad91707d99932209e5d3a35 Mon Sep 17 00:00:00 2001 From: Leopold Talirz Date: Fri, 29 May 2026 14:31:52 +0200 Subject: [PATCH 2/2] add mcp tool --- amorphouspy_api/src/amorphouspy_api/mcp_server.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/amorphouspy_api/src/amorphouspy_api/mcp_server.py b/amorphouspy_api/src/amorphouspy_api/mcp_server.py index 1c472ed4..2507437a 100644 --- a/amorphouspy_api/src/amorphouspy_api/mcp_server.py +++ b/amorphouspy_api/src/amorphouspy_api/mcp_server.py @@ -28,8 +28,9 @@ 1. `submit_job` — submit a simulation (returns job ID + URLs). 2. `get_job_status` — poll until status is "completed". 3. `get_job_results` — retrieve analysis data. -4. `search_jobs` — find existing results for similar compositions. -5. `list_glasses` / `lookup_glass` — browse available compositions. +4. `get_job_settings` — inspect the original submission parameters. +5. `search_jobs` — find existing results for similar compositions. +6. `list_glasses` / `lookup_glass` — browse available compositions. """ mcp = FastMCP( @@ -51,6 +52,7 @@ def register_tools() -> None: from amorphouspy_api.routers.jobs import ( cancel_job, get_job_results, + get_job_settings, get_job_status, get_single_result, search_jobs, @@ -62,6 +64,7 @@ def register_tools() -> None: search_jobs, get_job_status, cancel_job, + get_job_settings, get_job_results, get_single_result, list_glasses,