Part of ToxMCP Suite -> https://github.com/ToxMCP/toxmcp
O-QT MCP is the ToxMCP suite's specialized OECD QSAR Toolbox workflow engine. It is designed to be consumed by downstream orchestrators or any other MCP-compatible client.
Public MCP endpoint for the OECD QSAR Toolbox.
Run QSAR workflows, grouping/read-across dossiers, and audit-ready PDF reports through any MCP-aware agent (Claude Code, Codex CLI, Gemini CLI, etc.).
flowchart LR
subgraph Clients["Clients and Agents"]
Codex["Codex CLI / Desktop"]
Claude["Claude Code"]
Gemini["Gemini CLI"]
Orchestrator["Downstream orchestrator"]
end
subgraph API["O-QT MCP Service"]
Router["FastAPI + JSON-RPC\n/health, /mcp"]
Registry["Tool registry\ninputSchema + discovery"]
Primary["Primary workflow surface\nrun_oqt_multiagent_workflow"]
Secondary["Secondary expert tools\nprofilers, QSAR, grouping, PDF"]
end
subgraph Engine["OECD QSAR Toolbox Engine"]
Workflow["Workflow orchestration\nsearch, profiling, metabolism, QSAR"]
Grouping["Grouping and read-across dossier builder"]
Artifacts["Structured artifacts\nJSON, Markdown, PDF"]
end
subgraph Contracts["Contract Layer"]
Portable["Portable handoff schemas\nschemas/"]
Docs["Boundary docs\nREADME + docs/architecture.md"]
end
Toolbox["OECD QSAR Toolbox WebAPI"]
Clients --> Router
Router --> Registry
Registry --> Primary
Registry --> Secondary
Primary --> Workflow
Secondary --> Workflow
Secondary --> Grouping
Workflow --> Artifacts
Grouping --> Artifacts
Workflow --> Toolbox
Grouping --> Toolbox
Artifacts --> Portable
Portable --> Orchestrator
Docs --> Orchestrator
The current implementation follows a layered model:
run_oqt_multiagent_workflowis the primary workflow entrypoint for downstream automation.- Lower-level Toolbox tools remain public for expert use, debugging, and custom orchestration.
- Portable handoff schemas live under
schemas/and carry O-QT-owned evidence forward without turning O-QT into the suite orchestrator. - Final suite-level evidence synthesis, BER/WoE logic, and cross-module decisions belong above O-QT in a downstream orchestrator.
See docs/architecture.md for the fuller boundary and contract notes. See docs/oecd_alignment_review_2025.md for the OECD 2025 grouping/IUCLID gap analysis and the next contract-focused improvements. See docs/cross_suite_alignment_2026.md for the public contract patterns adopted into O-QT.
This release focuses on audit remediation and scientific governance controls.
- Human review checkpoints —
run_oqt_multiagent_workflownow supportsrequire_human_review=trueto pause atchemical_identity,ad_assessment, andfinal_reportcheckpoints before generating artifacts. - AD hard gating — Out-of-domain QSAR predictions block PDF generation when review mode is enabled.
- Privacy-aware logging — SMILES, CAS numbers, chemical names, and API keys are automatically hashed in audit logs and
httpxURL traces. - LLM prompt sanitization — Untrusted identifiers are scrubbed of control characters and formatting symbols before entering LLM-facing contexts.
- PDF provenance — Fallback PDF reports now include a disclaimer header, applicability-domain warnings, and a provenance summary.
- Safer search defaults —
search_typenow defaults to"name"instead of"auto".
This is a contract-hardening and validation release, not a redesign.
- Bumped the public package and runtime version to
0.3.0. - Hardened
oqtHazardEvidenceSummary.v1andoqtReadAcrossSummary.v1with machine-readableassessmentBoundary,decisionBoundary,decisionOwner,supports, andrequiredExternalInputs. - Added
semanticCoverageto the hazard uncertainty contract so the MCP states explicitly that O-QT uncertainty is qualitative evidence packaging, not probabilistic confidence. - Expanded normalized provenance and study packaging so hazard outputs can carry endpoint study records, endpoint summaries, evidence blocks, and source-call metadata in a more audit-ready shape.
- Added configurable wall-clock safeguards and a live smoke suite for the Toolbox-backed surface, including hazard analysis, workflow handoffs, grouping dossiers, and log replay.
- Refreshed the public docs so the README, schemas, and release notes reflect the current contract surface without referencing still-private modules.
Portable O-QT handoff objects are now published as machine-readable JSON Schemas under schemas/, with matching examples under schemas/examples/.
Published object family:
schemas/oqtHazardEvidenceSummary.v1.jsonschemas/oqtReadAcrossSummary.v1.jsonschemas/oqtWorkflowRecord.v1.json
Design intent:
- These are handoff objects, not final decision objects.
- O-QT owns Toolbox-native workflow evidence, grouping/read-across packaging, and provenance.
- Final suite-level evidence synthesis belongs in a downstream orchestrator, not inside O-QT MCP.
- Example instances live under
schemas/examples/and are validated in tests.
Chemical safety work often relies on the proprietary OECD QSAR Toolbox desktop application. Scientists have to click through many screens to gather profilers, metabolism simulators, and QSAR predictions before writing regulatory reports.
The O-QT MCP server turns that workflow into an open, programmable interface:
- Primary workflow entrypoint:
run_oqt_multiagent_workfloworchestrates the same multi-agent pipeline used in the O-QT AI Assistant. - Structured JSON + Markdown + PDF responses are returned in one call, ready for downstream automation.
- Expert helper surface: lower-level Toolbox tools remain available for debugging, specialist review, and custom orchestration.
- Vendor-neutral: any coding agent that speaks MCP can trigger analyses and capture outputs.
Looking for the original assistant UI? See O-QT-OECD-QSAR-Toolbox-AI-assistant. The MCP server reuses the same core logic but wraps it in a secure, headless API designed for automation.
Related publication: Artificial intelligence for integrated chemical safety assessment using OECD QSAR Toolbox.
bioRxiv preprint: 10.64898/2026.02.06.703989v1 (Ivo Djidrovski et al.).
| Capability | Description |
|---|---|
| 🔬 Primary workflow engine | Calls the OECD QSAR Toolbox WebAPI to run searches, profilers, metabolism simulators, curated QSAR models, and the flagship run_oqt_multiagent_workflow entrypoint. |
| 🧩 Grouping/read-across support | Builds OECD-style grouping dossiers through build_grouping_justification with structured similarity and uncertainty reporting. |
| 📦 Portable handoff contracts | Publishes stable cross-suite handoff schemas for downstream orchestrators and other contract consumers, including evidence blocks, applicability-domain review, and attachment manifests. |
| 🧾 Regulatory-ready reporting | Generates a comprehensive PDF (ReportLab), Markdown narrative, and JSON provenance bundle. |
| 🛡️ Enterprise security | OAuth2/OIDC token validation, RBAC per tool, audit logging, and Docker hardening. |
| 🤖 Agent friendly | Tested with Claude Code, Codex CLI, and Gemini CLI (see integration guide). |
- Architecture
- What's New In v0.3.1
- What's New In v0.3.0
- Published Schemas
- Quick start
- Related resources
- Configuration
- Public surface model
- Tool catalog
- Running the server
- Deployment modes
- Integrating with coding agents
- Downstream orchestration
- Output artifacts
- Human review checkpoints
- Security checklist
- Current limitations
- Development notes
- Roadmap
- License
poetry install
cp .env.example .env
# set QSAR_TOOLBOX_API_URL in .env
poetry run uvicorn src.api.server:app --reload --host 0.0.0.0 --port 8000
curl -s http://localhost:8000/health | jq .git clone https://github.com/ToxMCP/oqt-mcp.git
cd oqt-mcp
poetry install
cp .env.example .env
poetry run uvicorn src.api.server:app --reloadImportant: The server needs access to a running OECD QSAR Toolbox WebAPI instance (typically on a Windows host). Set
QSAR_TOOLBOX_API_URLin.envto point to it.
Once running, your MCP host connects to http://localhost:8000/mcp.
- ToxMCP suite overview: ToxMCP/toxmcp
- Original interactive UI (Streamlit app): VHP4Safety/O-QT-OECD-QSAR-Toolbox-AI-assistant
- Peer-reviewed publication: Artificial intelligence for integrated chemical safety assessment using OECD QSAR Toolbox
- bioRxiv preprint (Ivo Djidrovski et al.): 10.64898/2026.02.06.703989v1
| Variable | Required | Default | Description |
|---|---|---|---|
QSAR_TOOLBOX_API_URL |
✅ | http://localhost:5000 |
Base URL to the OECD QSAR Toolbox WebAPI. |
QSAR_LIGHT_TIMEOUT_SECONDS |
Optional | 30 |
Per-request timeout for lightweight Toolbox calls. |
QSAR_HEAVY_TIMEOUT_SECONDS |
Optional | 300 |
Per-request timeout for expensive Toolbox calls such as reports, workflows, and some simulations. |
QSAR_LIGHT_MAX_ATTEMPTS |
Optional | 2 |
Retry count for lightweight Toolbox calls. |
QSAR_HEAVY_MAX_ATTEMPTS |
Optional | 3 |
Retry count for expensive Toolbox calls. |
QSAR_HEAVY_CONCURRENCY |
Optional | 3 |
Concurrency cap for heavy Toolbox calls issued by the MCP. |
QSAR_HAZARD_PROFILING_WALLCLOCK_TIMEOUT_SECONDS |
Optional | 25 |
Wall-clock cap for the profiling sweep inside analyze_chemical_hazard; returns explicit partial evidence if exceeded. |
QSAR_DISCOVERY_LIST_ALL_TOTAL_WALLCLOCK_TIMEOUT_SECONDS |
Optional | 45 |
Total wall-clock budget for list_all_qsar_models. |
QSAR_DISCOVERY_LIST_ALL_PER_POSITION_TIMEOUT_SECONDS |
Optional | 6 |
Per-endpoint-tree-position timeout while enumerating the QSAR model catalog. |
QSAR_DISCOVERY_SEARCH_DATABASES_WALLCLOCK_TIMEOUT_SECONDS |
Optional | 20 |
Wall-clock cap for list_search_databases; fails fast on timeout. |
AUTH_OIDC_ISSUER |
✅ (prod) | – | OIDC issuer URL (Auth0, Keycloak, etc.). |
AUTH_OIDC_AUDIENCE |
✅ (prod) | – | Expected audience in access tokens. |
AUTH_OIDC_ALGORITHMS |
✅ (prod) | ["RS256"] |
Allowed JWT algorithms. |
AUTH_ROLE_CLAIM_PATH |
Optional | roles |
Dot path to extract role claims from the JWT. |
BYPASS_AUTH |
Dev only | false |
When true, skips auth and injects a SYSTEM_BYPASS role. |
AUTH_JWKS_CACHE_TTL_SECONDS |
Optional | 300 |
TTL for JWKS cache. |
LOG_LEVEL |
Optional | INFO |
Log verbosity. |
ENVIRONMENT |
Optional | development |
Included in logs and /health response. |
ASSISTANT_PROVIDER |
Optional | – | Set to OpenAI or OpenRouter to enable the legacy O-QT multi-agent workflow. |
ASSISTANT_MODEL |
Optional | gpt-4.1-nano |
LLM identifier to use when the assistant path is enabled. |
ASSISTANT_API_KEY |
Optional | – | API key for the selected provider. Falls back to OPENAI_API_KEY / OPENROUTER_API_KEY if absent. |
Compatibility note: the current client targets Toolbox WebAPI /api/v6 routes. Newer Toolbox builds are supported as long as they keep the v6 compatibility layer enabled.
Operational note: on slower Toolbox hosts, prefer tuning the wall-clock safeguards above rather than only increasing retry counts. The MCP is designed to return explicit partial evidence or timeout errors, not to block indefinitely on discovery-heavy endpoints.
See docs/auth_testing.md for token generation tips and bypass mode safety.
- Primary mode:
run_oqt_multiagent_workflowis the recommended default entrypoint for downstream automation. - Secondary helper tools: lower-level Toolbox tools remain public for expert use, debugging, and custom orchestration.
- Grouping/read-across:
build_grouping_justificationpackages O-QT-native dossier evidence without claiming suite-level final decisions. - Suite boundary: final evidence synthesis and decision logic belong to a downstream orchestrator, not to O-QT MCP itself.
| Tool | Description |
|---|---|
run_oqt_multiagent_workflow |
Executes the full O-QT multi-agent pipeline (search + profiling + optional QSAR) and returns structured JSON results, Markdown narrative, and a PDF report. |
build_grouping_justification |
Builds an OECD-style grouping/read-across dossier with structured context, similarity assessment, uncertainty reporting, and PDF output. |
list_profilers |
Lists profilers configured inside the OECD QSAR Toolbox. |
get_profiler_info |
Provides metadata, categories, and literature links for a specific profiler. |
list_simulators |
Lists metabolism simulators (e.g., liver, skin, microbial). |
get_simulator_info |
Provides detailed information for a simulator GUID. |
list_calculators |
Lists calculator modules for physicochemical property estimation. |
get_calculator_info |
Returns description, units, and notes for a calculator. |
get_endpoint_tree |
Returns the endpoint taxonomy used to organise profilers and models. |
get_metadata_hierarchy |
Returns the metadata hierarchy useful for filtering experimental data. |
list_qsar_models |
Lists QSAR models for a specific endpoint tree position. |
list_all_qsar_models |
Enumerates the full QSAR catalog across the endpoint tree (deduplicated). Returns partial catalog metadata and warnings if enumeration exceeds the configured wall-clock budget. |
list_search_databases |
Enumerates searchable inventories in the QSAR Toolbox. Fails fast on timeout rather than waiting through the full heavy retry budget. |
run_qsar_model |
Runs a specific QSAR model for a chemId and reports applicability domain status. |
run_profiler |
Executes a profiler for a chemId (optionally providing a simulator). |
run_metabolism_simulator |
Runs a metabolism simulator using either a chemId or SMILES. |
download_qmrf |
Retrieves the QMRF report for a QSAR model. |
download_qsar_report |
Retrieves the QSAR prediction report produced by the Toolbox. |
execute_workflow |
Runs a Toolbox workflow for a chemId. |
download_workflow_report |
Retrieves a workflow execution report. |
group_chemicals_by_profiler |
Builds read-across groups for a chemId using a profiler GUID. |
canonicalize_structure |
Returns the canonical SMILES for a structure. |
structure_connectivity |
Returns the connectivity string for the supplied SMILES. |
render_pdf_from_log |
Generates the regulatory PDF from a stored comprehensive log (no rerun). |
build_portable_handoffs_from_log |
Reconstructs schema-aligned portable_handoffs from a stored O-QT log bundle (no rerun). |
| Parameter | Type | Required | Description |
|---|---|---|---|
identifier |
string | ✅ | Chemical identifier (name, CAS, or SMILES). |
search_type |
enum(name, cas, smiles) |
✅ | How to interpret identifier. |
context |
string | – | Free-form text describing the analysis context. |
profiler_guids |
array[string] | – | Explicit profilers to run. |
qsar_mode |
enum(recommended,all,none) |
– | QSAR preset (defaults to curated recommended). |
qsar_guids |
array[string] | – | Exact QSAR model GUIDs. |
simulator_guids |
array[string] | – | Metabolism simulators to execute. |
llm_provider |
string | – | Override LLM provider (e.g., openai, openrouter). |
llm_model |
string | – | LLM model identifier. |
llm_api_key |
string | – | API key when not provided via environment. |
require_human_review |
boolean | – | When true, high-risk checkpoints require explicit approval before artifacts are generated. |
workflow_id |
string | – | Optional workflow ID for resuming a review-paused workflow. |
checkpoint_approvals |
array[{checkpoint_id, decision, comments}] | – | Pre-approved checkpoints to resume a paused workflow. |
| Parameter | Type | Required | Description |
|---|---|---|---|
identifier |
string | ✅ | Target chemical identifier (name, CAS, or SMILES). |
search_type |
enum(auto,name,cas,smiles) |
– | How to interpret the target identifier. |
problem_formulation |
string | ✅ | Intended use of the grouping or read-across exercise. |
decision_context |
string | ✅ | Context such as screening, hazard identification, or risk assessment. |
endpoints |
array[string] | ✅ | Endpoint list to justify in the dossier. |
route_of_exposure |
string | – | Optional route of exposure relevant to the endpoints. |
grouping_hypothesis |
string | ✅ | Why the target and source chemicals are expected to be sufficiently similar. |
analogue_identifiers |
array[string] | – | Candidate source analogues or category members to resolve in the Toolbox. |
analogue_search_type |
enum(auto,name,cas,smiles) |
– | How to interpret the analogue identifiers. |
profiler_guids |
array[string] | – | Profilers used to support the similarity rationale. |
simulator_guids |
array[string] | – | Metabolism simulators used to support ADME/TK similarity. |
qsar_guids |
array[string] | – | QSAR models used as supporting evidence for the target substance. |
accepted_uncertainty_level |
enum(low,medium,high) |
– | Maximum residual uncertainty tolerated for the stated purpose. |
context |
string | – | Optional extra narrative instructions for the dossier. |
summary_markdown– same narrative presented in the assistant UI.log_json– comprehensive bundle. When the assistant workflow runs, the payload includes:assistant_session(provider/model, duration, specialist outputs)mcp_workflow(deterministic fallback summary and Toolbox metadata)analysis,data_retrieval, and other sections reused by the original app.
build_grouping_justificationadds agrouping_justificationobject with target/analogue resolution, similarity assessment, endpoint conclusions, and uncertainty reporting.pdf_report_base64– base64-encoded, publication-ready PDF.portable_handoffs– schema-aligned handoff objects for downstream orchestration.build_grouping_justificationreturnsoqtWorkflowRecord.v1plusoqtReadAcrossSummary.v1;run_oqt_multiagent_workflowreturnsoqtWorkflowRecord.v1plusoqtHazardEvidenceSummary.v1.oqtWorkflowRecord.v1now declaresrootEntity,packageSemantics, and an auditableattachmentsmanifest with checksums when the inline payload is available.oqtHazardEvidenceSummary.v1now carries request metadata, explicit assessment and decision boundaries, support claims, required external inputs, endpoint summaries, source attribution for model/profiler/simulator evidence, and a qualitative uncertainty block withsemanticCoveragethat explicitly states it is an evidence-completeness assessment rather than a probabilistic estimate.oqtReadAcrossSummary.v1now promotes explicit assessment and decision boundaries, required external inputs, applicability-domain notes, the portable evidence matrix, and the aspect-level uncertainty table into the published handoff contract.- Portable downstream contracts are published separately under
schemas/; see schemas/README.md.
The MCP exposes normalized provenance wherever the Toolbox already returns ownership or study metadata:
get_public_qsar_model_info,get_profiler_info,get_simulator_info, andget_calculator_infoinclude a top-levelprovenanceblock.list_qsar_modelsandlist_all_qsar_modelsattach a compactprovenance_summaryto each catalog record.run_qsar_prediction,run_qsar_model,download_qmrf, anddownload_qsar_reportincludemodel_provenance.run_profilerandgroup_chemicals_by_profilerincludeprofiler_provenance.generate_metabolitesandrun_metabolism_simulatorincludesimulator_provenance.analyze_chemical_hazardnow normalizes endpoint study records, endpoint summaries, evidence blocks, applicability-domain review, provenance collections, and a portableoqtHazardEvidenceSummary.v1handoff alongside the raw Toolbox payloads. The helper is intentionally bounded: ifprofiling/allis too slow, it returns partial endpoint evidence with explicit uncertainty and warning fields instead of waiting indefinitely.- Hazard and read-across handoffs now publish machine-readable boundary fields (
assessmentBoundary,decisionBoundary,decisionOwner) plussupportsandrequiredExternalInputs, so downstream systems can distinguish what O-QT packaged from what still requires expert review, regulatory policy, exposure context, or cross-module synthesis. - High-level workflow outputs preserve the same normalized metadata inside
log_json.profiler_results[*].profiler_provenance,log_json.simulator_results[*].simulator_provenance, andlog_json.qsar_results[*].model_provenance. - Report-style tools (
download_qmrf,download_qsar_report,download_workflow_report) now also declarecontent_type; when the Toolbox returns a ZIP bundle rather than a bare PDF, the MCP response includesarchive_entriesandpdf_report_base64when a PDF member can be extracted. - High-level workflows and grouping dossiers accept direct Toolbox
chemIdvalues asidentifierinputs, which lets orchestrators bypass flaky search endpoints once a substance has already been resolved.
poetry install
poetry run uvicorn src.api.server:app --host 0.0.0.0 --port 8000 --reloadOnce the server is running on http://localhost:8000/mcp (and your .env points to a reachable Toolbox WebAPI), the following curl invocations exercise the main tools with Benzene as an example:
# 1. Handshake and tool discovery
curl -s http://localhost:8000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}}'
curl -s http://localhost:8000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"mcp/tool/list","params":{}}' | jq '.result.tools | length'
# 2. Resolve Benzene and pull discovery metadata
BENZENE_CHEMID=$(
curl -s http://localhost:8000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"mcp/tool/call","params":{"name":"search_chemicals","parameters":{"query":"Benzene","search_type":"name"}}}' \
| jq -r '.result.content[0].text | fromjson | .items[0].ChemId'
)
echo "$BENZENE_CHEMID"
curl -s http://localhost:8000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":4,"method":"mcp/tool/call","params":{"name":"list_profilers","parameters":{}}}' \
| jq -r '.result.content[0].text | fromjson | .profilers[:5]'
# 3. Execute profilers / QSAR workflow (uses chemId from the first search hit)
PROFILER_GUID="a06271f5-944e-4892-b0ad-fa5f7217ec14"
curl -s http://localhost:8000/mcp \
-H 'Content-Type: application/json' \
-d "{\"jsonrpc\":\"2.0\",\"id\":5,\"method\":\"mcp/tool/call\",\"params\":{\"name\":\"run_profiler\",\"parameters\":{\"profiler_guid\":\"$PROFILER_GUID\",\"chem_id\":\"$BENZENE_CHEMID\"}}}" \
| jq -r '.result.content[0].text | fromjson | .result'
curl -s http://localhost:8000/mcp \
-H 'Content-Type: application/json' \
-d "{\"jsonrpc\":\"2.0\",\"id\":6,\"method\":\"mcp/tool/call\",\"params\":{\"name\":\"run_oqt_multiagent_workflow\",\"parameters\":{\"identifier\":\"Benzene\",\"search_type\":\"name\",\"profiler_guids\":[\"$PROFILER_GUID\"]}}}" \
| jq -r '.result.content[0].text | fromjson | {status: .status, summary: .summary_markdown, pdf_bytes: (.pdf_report_base64 | length)}'
# 4. Optional helpers
curl -s http://localhost:8000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":7,"method":"mcp/tool/call","params":{"name":"canonicalize_structure","parameters":{"smiles":"c1ccccc1"}}}' \
| jq -r '.result.content[0].text | fromjson'
# 5. Build an OECD-style grouping/read-across dossier
curl -s http://localhost:8000/mcp \
-H 'Content-Type: application/json' \
-d "{\"jsonrpc\":\"2.0\",\"id\":8,\"method\":\"mcp/tool/call\",\"params\":{\"name\":\"build_grouping_justification\",\"parameters\":{\"identifier\":\"Benzene\",\"search_type\":\"name\",\"problem_formulation\":\"Assess whether close aromatic hydrocarbon analogues can support exploratory read-across for repeated-dose toxicity.\",\"decision_context\":\"hazard_identification\",\"endpoints\":[\"Repeated dose toxicity\"],\"route_of_exposure\":\"oral\",\"grouping_hypothesis\":\"Target and source analogues are simple aromatic hydrocarbons expected to share related structural, mechanistic, and metabolic features.\",\"analogue_identifiers\":[\"Toluene\",\"Ethylbenzene\"],\"analogue_search_type\":\"name\",\"profiler_guids\":[\"$PROFILER_GUID\"],\"accepted_uncertainty_level\":\"medium\"}}}" \
| jq -r '.result.content[0].text | fromjson | {status: .status, uncertainty: .grouping_justification.uncertainty_assessment.overall_level, structure: .grouping_justification.structure_comparison.summary, physchem: .grouping_justification.physicochemical_comparison.summary}'
# 6. Rebuild portable handoffs from a stored log bundle (no Toolbox rerun)
WORKFLOW_RESPONSE=$(
curl -s http://localhost:8000/mcp \
-H 'Content-Type: application/json' \
-d "{\"jsonrpc\":\"2.0\",\"id\":9,\"method\":\"mcp/tool/call\",\"params\":{\"name\":\"run_oqt_multiagent_workflow\",\"parameters\":{\"identifier\":\"Benzene\",\"search_type\":\"name\",\"profiler_guids\":[\"$PROFILER_GUID\"]}}}"
)
WORKFLOW_LOG=$(echo "$WORKFLOW_RESPONSE" | jq -c '.result.content[0].text | fromjson | .log_json')
curl -s http://localhost:8000/mcp \
-H 'Content-Type: application/json' \
-d "{\"jsonrpc\":\"2.0\",\"id\":10,\"method\":\"mcp/tool/call\",\"params\":{\"name\":\"build_portable_handoffs_from_log\",\"parameters\":{\"log\":$WORKFLOW_LOG}}}" \
| jq -r '.result.content[0].text | fromjson | .portable_handoffs | keys'You should see:
- Tool listing reporting around 31 tools (including compatibility aliases).
search_chemicalsresolving Benzene to a ToolboxchemId.- Profiler execution returning the “Class 1 (narcosis or baseline toxicity)” call.
run_oqt_multiagent_workflowproducing a Markdown summary along with a non-empty Base64 PDF payload (written to disk automatically when invoked via Codex, Claude, or Gemini CLIs).build_portable_handoffs_from_logregeneratingoqtWorkflowRecord.v1and the matching domain handoff object from a storedlog_json.GET /mcpreturning405 Method Not Allowedis expected; use JSON-RPCPOSTrequests.
docker build -t o-qt-mcp-server .
docker run -d --name o-qt-mcp \
--env-file .env \
-p 8000:8000 \
o-qt-mcp-serverThe MCP can reuse the multi-agent prompts and PDF template from the original O-QT assistant. Configure the following before starting the server:
export ASSISTANT_PROVIDER=OpenAI # or OpenRouter
export ASSISTANT_MODEL=gpt-4.1-nano # any model supported by the provider
export ASSISTANT_API_KEY=sk-... # falls back to OPENAI_API_KEY/OPENROUTER_API_KEYWith these variables set, run_oqt_multiagent_workflow will call the same specialist agents used by the Streamlit app, return the full assistant transcript inside log_json.assistant_session, and embed the assistant-generated PDF in pdf_report_base64. If the assistant cannot run (missing key, upstream error, etc.) the MCP automatically falls back to the deterministic summary.
docker compose up --buildThis launches:
| Service | Purpose | Port |
|---|---|---|
mcp-server |
The MCP server | 8000 |
toolbox-stub |
Mock Toolbox WebAPI for demos | 5000 |
Update .env to point at a real Toolbox instance before production use.
| Profile | Shape | Suitable for | Notes |
|---|---|---|---|
local-dev |
Single FastAPI process, optional auth bypass | Workstation development, smoke tests, prompt-driven debugging | Calls the Toolbox WebAPI synchronously. |
controlled-prod |
FastAPI behind TLS reverse proxy with OIDC, RBAC, and audit shipping | Internal deployments and orchestrated suite flows | Still synchronous in v0.3.0; scale with worker policy and operational guardrails rather than a built-in queue. |
not-in-v0.3.0 |
Async queue and persistence layer | Detached batch jobs and large-scale job replay | Remains roadmap work. |
This release intentionally tightens the deployment story instead of over-claiming platform maturity.
Follow docs/integration_guides/mcp_integration.md for step-by-step instructions covering:
- Claude Code / Cursor
- Codex CLI
- Gemini CLI
- Generic MCP hosts
Each guide includes JSON snippets for provider configuration and tips for handling OAuth tokens.
O-QT is a module-scoped engine inside the broader suite. A typical orchestrated flow is:
- A downstream orchestrator calls
run_oqt_multiagent_workflowfor Toolbox-native hazard evidence orbuild_grouping_justificationfor read-across support. - O-QT returns live MCP artifacts and a
portable_handoffsblock aligned with the published contracts underschemas/. - The orchestrator combines O-QT output with other suite evidence before any higher-level synthesis or decision support step.
See docs/integration_orchestrators.md for a worked example.
{
"oqt_module_role": "specialized OECD QSAR Toolbox engine",
"default_entrypoint": "run_oqt_multiagent_workflow",
"published_handoff_objects": [
"oqtWorkflowRecord.v1",
"oqtHazardEvidenceSummary.v1",
"oqtReadAcrossSummary.v1"
],
"suite_orchestrator_role": "A downstream orchestrator combines O-QT with other evidence sources"
}Every successful run returns three artifacts:
- JSON log – Raw payloads from the QSAR Toolbox plus the specialist agent outputs.
- Markdown narrative – Human-readable synthesis suitable for reports or version control.
- PDF report – Built with ReportLab; includes provenance tables, key study badges, and optional logo.
Consumers can store the PDF by decoding pdf_report_base64 from the tool response.
Portable cross-suite contract files are versioned separately under schemas/.
For scientific governance, run_oqt_multiagent_workflow supports an optional require_human_review=true mode that pauses the workflow at high-risk decision points instead of auto-generating artifacts.
chemical_identity— After resolving the input identifier to a Toolbox record.ad_assessment— When any QSAR prediction reportsad_warning=true(out of applicability domain).final_report— Before generating the PDF artifact.
- If no checkpoints are triggered, the workflow completes normally (
status: "ok"). - If checkpoints are pending, the workflow returns:
status: "review_required"workflow_id— ID to use when resumingreview_checkpoints— List of pending checkpoints with metadata- No PDF is generated.
Use the approve_workflow_checkpoint tool:
{
"name": "approve_workflow_checkpoint",
"arguments": {
"checkpoint_id": "<checkpoint-id>",
"decision": "approved",
"comments": "Looks correct"
}
}Decisions: approved | rejected
Pass the same workflow_id back to run_oqt_multiagent_workflow (with require_human_review=true). The server will detect approved checkpoints and complete the workflow, returning status: "ok" and the PDF.
Note: Checkpoint state is held in memory. If the server restarts, pending checkpoints are lost. Do not use this feature for long-lived review cycles without external persistence.
- ✅ Use OAuth2/OIDC in production (
BYPASS_AUTH=false). - ✅ Terminate TLS at a reverse proxy.
- ✅ Configure RBAC in
config/tool_permissions.default.json. - ✅ Enable audit log shipping (see docs/observability.md).
- ✅ Turn on
require_human_review=truefor high-stakes workflows to enforce explicit checkpoint approval. - ✅ Rotate secrets via platform-specific secret stores.
- ✅ Regularly update the Docker base image (see Dockerfile).
- Execution is synchronous request/response only; O-QT does not yet ship a built-in queue or persistence layer.
- A licensed OECD QSAR Toolbox installation with a reachable WebAPI is still required.
- Portable schemas are published contracts for downstream handoff; they do not replace suite-level synthesis or decision logic.
| Command | Purpose |
|---|---|
poetry run pytest |
Run the full test suite. |
poetry run pytest tests/auth -q |
Focus on authentication tests. |
poetry run black . && poetry run isort . |
Format code. |
docker compose up --build |
Local stack with Toolbox stub. |
Additional documentation:
- docs/architecture.md – module boundary, suite role, and contract notes.
- docs/integration_orchestrators.md – downstream orchestration example.
- docs/testing.md – local tooling and CI details.
- docs/release_process.md – versioning and release checklist.
- schemas/README.md – portable handoff schema inventory.
- docs/toolbox_webapi_overview.md – mapping MCP tools to Toolbox endpoints.
- SECURITY.md – vulnerability reporting policy.
- Streaming progress updates over MCP notifications for long-running QSAR jobs.
- Stronger contract gates to keep live MCP outputs and published handoff schemas aligned.
- Optional job queue and persistence layer for asynchronous execution.
Community feedback and pull requests are welcome. See CONTRIBUTING.md and CODE_OF_CONDUCT.md for details.
Ivo Djidrovski
This project is released under the Apache License 2.0.
OECD QSAR Toolbox is proprietary software. Users must supply their own licensed installations and comply with the OECD EULA.
{ "status": "ok", "identifier": "Acetone", "summary_markdown": "...", "log_json": { "...": "..." }, "pdf_report_base64": "JVBERi0xLjcKJ...", "portable_handoffs": { "oqtWorkflowRecord.v1": { "...": "..." }, "oqtHazardEvidenceSummary.v1": { "...": "..." } } }