Intel MCP
by tarous89
README.md
# Intel MCP
Remote Model Context Protocol service for TrialAgents Intel Agent.
The service root serves the public Intel MCP documentation page. It explains
the report-run lifecycle, platform-specific ChatGPT and Claude connector setup,
an official Python MCP SDK example, copyable tool arguments and the complete
six-tool workflow. Hosted ChatGPT and Claude clients sign in through TrialAgents
OAuth linked to the existing Intel Agent account; the page never exposes or asks a
customer to reuse the internal service credential.
Implemented tools:
- `start_analysis` receives only an app-created `report_run_id`, calls the Intel Agent app's service-authenticated control plane, and returns the existing or newly reserved tier-bounded analysis lease.
- `filter_trials` deterministically queries approved structured Trial Profiles through the Engine-owned `mcp_serving` v1 read contract. It then asks the app control plane to validate the `analysis_id` and atomically meter the unique trial IDs that may be returned.
- `classify_trials` classifies approved contact-redacted Trial Profiles against bounded user criteria and returns deterministic eligible/ineligible/uncertain trial ID buckets with counts.
- `get_profiles` returns current approved Trial Profile 11.0.0 data for 1–10 explicit EU trial numbers per call. Optional `sections` returns exact deterministic profile projections; omitting `sections` returns the complete profile. Light may retrieve 100 unique profiles across an analysis.
- `get_documents` returns extracted text for one explicitly named document, in parts of at most 200,000 characters, and meters unique documents through the app control plane.
- `extract_variables` extracts up to 20 typed values from one approved trial in one Terra request using only its complete approved Trial Profile.
User identity, plan approval, package, enabled tools and allowances remain app-owned. MCP has no app/control-plane database credential and no Engine owner or write credential. Its only clinical-store login is the exact `intel_mcp_reader_v1` role, which PostgreSQL restricts to approved-only versioned views and read-only transactions.
Every MCP business-tool invocation emits best-effort operational telemetry to the
app control plane: response duration, success/error code and, for worker-backed tools,
the selected model plus actual Responses API token usage. Clinical inputs and results
are never included. The app reservation response selects the model independently for
`classify_trials` and `extract_variables`, so admin changes apply on the next call
without restarting MCP.
## `extract_variables` contract
`extract_variables` accepts one `trial_id` and 1–20 variable definitions with a
snake-case name, precise instruction and optional value type. Supported types
are string, integer, number, boolean and string array.
The local Engine-read adapter supplies only the complete approved Trial Profile;
protocol and other source-document text are never retrieved or forwarded. Output is
limited to the trial ID, a values object containing every requested key (with
`null` when unresolved), and the standard analysis allowance. Status,
explanation, evidence, document name, page and source metadata are excluded from
both the worker schema and public result.
No on-demand download, OCR or extraction occurs. The tool uses one model request
per invocation with no automatic model retry. Stable trial-plus-variable-set
keys make exact retries allowance-safe. Detailed contract:
`docs/extract-variables.md`.
## `filter_trials` contract
`filter_trials` is a shortlist tool. It does not search the whole profile, run semantic search, classify trials, retrieve complete profiles/documents, extract variables or write report prose.
Use it as the first screening step. Apply broad structured conditions to reduce the approved-profile population to a focused shortlist, then use `classify_trials` for complex inclusion/exclusion logic or `get_profiles` with selected sections when the shortlist needs objective-specific profile review. Classification accepts at most 25 trials per call, so split larger shortlists into consistent batches rather than classifying the full discovery population.
Each shortlist item contains only `eu_number`, `trial_title` and `sponsor_name`.
Document names, phase and dates are not repeated in filtering results. Retrieve relevant
profile sections or a complete selected profile with `get_profiles`; its
`filtering_variables.available_extracted_documents` object is available through the
`documents` section and contains the exact names accepted by `get_documents`. Output counts contain
total approved profiles, total matches and records returned in this call. Analysis
allowance separately reports its limit, cumulative unique IDs used and remaining capacity.
General behavior:
- Only `approval_status = approved` Trial Profiles are eligible.
- All text comparisons are case-insensitive.
- `contains` is the default text operator; `is` means a complete case-insensitive match.
- Negative text operators are `does_not_contain` and `is_not`. A missing value never satisfies a negative filter.
- Controlled-array operators are `contains_any`, `contains_all` and `contains_none`.
- Different fields combine with AND. Put multiple alternatives for the same field in one condition. If OR is needed across different fields, make separate calls.
- Conditions within one `countries` group must match the same country row. Multiple country groups combine with AND and may match different rows.
- Default order is `latest_country_submission_or_approval_date desc`, with `eu_number asc` as the stable tie-breaker.
- Pages are capped at 100. Use `offset: 0` first, then increase offset by the prior call's limit while more matches remain.
- Light may receive at most 100 unique filtered trial IDs. The current Max candidate workflow may receive up to 1,000. Repeated IDs do not consume allowance twice.
- The MCP annotation uses `readOnlyHint: false`: the Engine query is read-only, but admitting a previously unseen trial ID updates the analysis's observable allowance state.
Exposed structured fields:
- Text: `eu_number`, `trial_title`, `trial_acronym`, `sponsor_name`.
- Dates: latest country submission/approval, initial CTIS submission, first CTIS authorization and latest CTIS authorization.
- Document availability: normalized extracted document types and individual document names.
- Controlled arrays: therapeutic areas, phases, administration routes, country codes, eligible sexes and comparator types. The `modalities` filter targets the profile's single scalar `modality` through the Engine compatibility projection.
- Booleans: rare-disease, orphan-designation, paediatric and first-in-human flags. Boolean values may also be checked for `unknown`.
- Numbers: planned sample size, number of countries and number of sites.
- Controlled scalars: allocation, masking and intervention model.
- Same-country fields: country code, normalized recruitment status, country dates, country site count and country planned sample size.
Controlled vocabularies are embedded directly in the MCP JSON Schema. Country codes use ISO 3166-1 alpha-2. Known normalized country statuses are `Authorised`, `Not authorised`, `Under evaluation`, `Ended`, `Halted`, `Lapsed`, `Withdrawn`, `Expired`, `Suspended`, `Not valid`, `Pending` and `Revoked`.
The controlled filter vocabularies are aligned with Trial Profile contract
11.0.0. The 34 therapeutic areas include separate Blood Disorders, Gynecology, Obstetrics,
Reproductive Medicine, Emergency Medicine and Critical Care values.
Sponsor-name limitation: the structured CTIS sponsor value can sometimes refer to a subsidy or funding source, or omit part of the complete legal entity name. Use sponsor-name filtering to shortlist records; do not treat it as definitive legal-entity resolution.
## `get_profiles` contract
`get_profiles` accepts `analysis_id`, `trial_ids`, and optional `sections`.
- Request **1–10 EU trial numbers per call**, regardless of whether `sections` is supplied.
- Duplicate trial IDs and section names are removed while preserving first occurrence order.
- With `sections`, the tool returns an exact deterministic projection of the stored profile. It performs no LLM summarization, rewriting or inference.
- With `sections` omitted or `[]`, the tool returns the complete stored current approved Trial Profile, including contacts, extracted-document inventory and results.
- Candidate/rejected/missing profiles are reported in `unavailable_trial_ids`; there is no raw-CTIS fallback.
- Light may retrieve **100 unique profiles across the analysis**; the current Max candidate workflow may retrieve **500**. Exact repeated IDs do not consume allowance twice, even if a later call requests different sections or the complete profile.
- Every approved profile admitted by the allowance is returned without field-level truncation within the requested projection. Unavailable IDs and IDs blocked because allowance was reached are returned as separate ID arrays.
- The tool does not refresh profiles, retrieve document text, classify, search semantically, extract variables or write report prose.
- Because returning a newly seen profile updates observable allowance state, annotations are non-read-only, non-destructive, idempotent and closed-world.
The only input added to the original tool is optional `sections`. The output contract is unchanged: `profiles`, `unavailable_trial_ids`, `allowance_reached_trial_ids`, `counts`, and `analysis_allowance` remain exactly as before.
### Trial Profile 11.0.0 section vocabulary
Within the `sites` section, every site-level person is stored under `classification_variables.sites[].investigators[]` and is a principal investigator by contract. There is no separate PI-status boolean.
- `overview` — therapeutic area, phase, disease, trial title/acronym and core flags.
- `population` — target population, stage/severity, settings, population characteristics, biomarkers and eligible sexes.
- `trial_design` — sample size, allocation, masking, intervention model and comparator types.
- `interventions` — modality, administration routes, targets, mechanisms and products.
- `eligibility` — inclusion and exclusion criteria.
- `objectives` — primary and secondary objectives.
- `endpoints` — structured endpoints.
- `sponsor_and_organizations` — sponsor, legal representative and third-party organizations.
- `contacts` — management, scientific, recruitment and public CTIS contacts.
- `countries` — country counts/codes and structured country records.
- `sites` — site count and structured site records with nested investigators.
- `documents` — six-category `available_extracted_documents` inventory.
- `lifecycle` — complete dated `ctis_lifecycle` object.
- `results` — complete results object, including participant flow, endpoint/safety results and operational findings.
Example projection request:
```json
{
"analysis_id": "ana_...",
"trial_ids": ["2024-500001-00-00", "2024-500002-00-00"],
"sections": ["overview", "trial_design", "endpoints", "countries"]
}
```
Example complete-profile request:
```json
{
"analysis_id": "ana_...",
"trial_ids": ["2024-500001-00-00"]
}
```
Production and the Engine profile-read boundary are both capped at ten trial IDs per request. Detailed contract: `docs/get-profiles.md`.
## `get_documents` contract
`get_documents` accepts `analysis_id`, one `trial_id`, one exact
case-insensitive `document_name`, and optional one-based `part` (default 1).
- The document must be listed in one of the approved Trial Profile's six
`filtering_variables.available_extracted_documents` arrays. Obtain the exact name with
`get_profiles(sections=["documents"])` or a complete profile before calling `get_documents`.
- Each response returns extracted text only, with preserved page markers, and
never returns a PDF, binary, link, page count or character count.
- Each part is limited to 200,000 characters. If `next_part` is a number, call
the tool again with that part; `null` means the document is complete.
- Light analyses may retrieve 10 unique documents; Max analyses may retrieve
50. Continuation parts and exact retries do not consume another document.
- The tool performs no on-demand download, OCR, extraction, semantic search or
model work.
Detailed contract: `docs/get-documents.md`.
## Local setup
```bash
python -m venv .venv
. .venv/bin/activate
pip install -e '.[dev]'
export INTEL_APP_CONTROL_URL=http://localhost:3000
export INTEL_APP_SERVICE_TOKEN=replace-me
# Production uses the database source. The Engine host/user/password are a
# distinct restricted login, never the Engine owner DATABASE_URL.
export MCP_ENGINE_SOURCE=database
export MCP_ENGINE_DATABASE_HOST=engine-db.internal
export MCP_ENGINE_DATABASE_NAME=intel
export MCP_ENGINE_DATABASE_USER=intel_mcp_reader_v1
export MCP_ENGINE_DATABASE_PASSWORD=replace-with-a-long-random-reader-password
# Optional authenticated HTTP rollback path:
export INTEL_ENGINE_API_URL=http://localhost:10000
export INTEL_ENGINE_SERVICE_TOKEN=replace-with-a-separate-engine-service-token
export MCP_INBOUND_SERVICE_TOKEN=replace-me-too
export REPORT_PLAN_SERVICE_TOKEN=replace-with-a-separate-report-plan-token
export MCP_PUBLIC_RESOURCE_URL=https://mcp.trialagents.com/mcp
export MCP_OAUTH_AUTHORIZATION_SERVER_URL=https://intel.trialagents.com
intel-mcp
```
The public documentation page is `/`, the Streamable HTTP endpoint is `/mcp`,
and the unauthenticated liveness endpoint is `/health`.
Required production settings:
- `INTEL_APP_CONTROL_URL`: service-authenticated Intel Agent app base URL.
- `INTEL_APP_SERVICE_TOKEN`: shared service credential stored only in Render secrets.
- `MCP_ENGINE_SOURCE`: `database` in production; `http` is retained only as a reversible cutover fallback.
- `MCP_ENGINE_DATABASE_HOST`, `MCP_ENGINE_DATABASE_PORT`, `MCP_ENGINE_DATABASE_NAME`: Engine PostgreSQL endpoint coordinates.
- `MCP_ENGINE_DATABASE_USER`: must be exactly `intel_mcp_reader_v1`; the service rejects owner or broader logins.
- `MCP_ENGINE_DATABASE_PASSWORD`: separate long reader password provisioned by the Engine migration tooling.
- `MCP_ENGINE_DATABASE_SSLMODE`: defaults to `require`.
- `INTEL_ENGINE_API_URL` and `INTEL_ENGINE_SERVICE_TOKEN`: authenticated HTTP rollback path; do not reuse the extraction run token.
- `MCP_INBOUND_SERVICE_TOKEN`: private server-to-server bearer retained for the Intel Agent backend.
- `REPORT_PLAN_SERVICE_TOKEN`: separate private bearer used only by the App's Report-plan endpoint.
- `MCP_PUBLIC_RESOURCE_URL`: canonical OAuth protected-resource audience for `/mcp`.
- `MCP_OAUTH_AUTHORIZATION_SERVER_URL`: Intel Agent account authorization-server issuer.
- `MCP_ALLOWED_HOSTS`: comma-separated exact public/private Host allowlist entries.
- `PORT`: HTTP port assigned by the platform.
`/mcp` accepts either the private internal service bearer or a scoped public OAuth access token issued by Intel Agent. Public clients discover OAuth through RFC 9728 protected-resource metadata; internal service credentials are never used as end-user tokens.
The private `POST /internal/report-plan` route accepts only
`REPORT_PLAN_SERVICE_TOKEN`. It sends the user's brief, requested insights and the
versioned description of all six MCP capabilities to `gpt-5.6-sol`, and returns only
the strict user-facing Report-plan structure. It is not an MCP tool and does not execute
clinical-data operations. New plans are v4. Planned and executed analyses must stay medical,
clinical-development or trial-operational; database-completeness analyses are prohibited and
unsupported requests are replaced with supported medical alternatives.
## Engine read isolation
The five clinical reads (`filter_trials`, classification profiles, complete/profile-section reads,
document text and extraction source) run inside MCP to remove a cold-starting Engine
web hop. Engine still owns ingestion, extraction, profile generation, approval, queues,
schema migrations and all writes. PostgreSQL enforces the split: the MCP role can select
only `mcp_serving.*_v1`, every MCP checkout begins `SET TRANSACTION READ ONLY`, and MCP
startup rejects any database URL whose username is not the restricted role. See
`docs/ENGINE_READ_CUTOVER.md` for the current boundary and deliberate rollback.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues