MCP server for NCI Clinical Trial Search API with Code Mode execution, GraphQL/SPARQL support, and API catalog search
Server defines 4 tools with detailed descriptions and complex parameter schemas. However, tool naming is inconsistent (3 tools share the same name '{prefix}_execute' with different modes), descriptions are verbose (>1000 chars, violating the 10-1024 char guideline), and output schemas are not formally documented. Parameter descriptions are present but lack clarity on return types and error conditions. No evidence of error handling guidance or recovery patterns.
Execute JavaScript code against the GraphQL API with sandboxed V8 isolate. Code runs with: gql.query(queryString, variables?) — GraphQL execution through host; schema.types(), schema.type(), schema.search() etc. — introspection helpers; db.stage(), db.queryStaged(), api.query() — staging helpers; console.log() capture. Optionally also a SECOND, REST upstream (api.get/api.post) routed through the same host proxy + DO-SQLite staging (hybrid GraphQL+REST). API keys never enter the isolate — all HTTP goes through the host's gqlFetch.
Execute JavaScript code against the SPARQL endpoint with sandboxed V8 isolate. Code runs with: sparql.query(queryString, opts?) — run SELECT/CONSTRUCT/DESCRIBE; returns array of plain { variable: value } rows; sparql.ask(queryString) — run ASK; returns boolean; sparql.raw(queryString, opts?) — return unparsed { head, results } envelope; prefixes — object of common ontology prefixes (rdfs, owl, xsd, skos, dcterms, void, obo, uberon, go, ncbigene, efo, obi, sio, up, ensembl, bgee); prefixHeader — pre-built `PREFIX …` block to prepend to your queries; console logging (log, warn, error, info) — captured output; fs.readFile / fs.writeFile / fs.glob ... — persistent virtual filesystem (when fsDoNamespace provided). The last expression or return value is the result. STAGING: Large SPARQL responses (>30KB) are auto-staged into SQLite. When this happens, sparql.query returns {__staged: true, data_access_id, schema, tables_created, total_rows, message}. Use api.query(data_access_id, sql) in-band, or return the staging object so the caller can use {prefix}_query_data.
Execute JavaScript code against the API with sandboxed V8 isolate. Code runs with: searchSpec(query) / searchPaths(query) — search the API catalog or OpenAPI spec; listCategories() / listTags() — inspect tags/categories; getEndpoint(path) / getOperation(idOrPath) — get endpoint docs; describeEndpoint(path, method?) / describeOperation(idOrPath) — format endpoint docs; spec — full frozen OpenAPI spec object; api.get(path, params) for GET, api.post(path, body, params) for POST; Path params like /lookup/{id} are auto-interpolated from params; Remaining params become query string; Large responses (>100KB) are auto-staged: check result.__staged, return the staging info, use {prefix}_query_data to explore; Use limit/pagination params to control response size. Large datasets auto-stage for SQL queries.
Three tools share identical name '{prefix}_execute' with different modes (REST, GraphQL, SPARQL). LLMs cannot distinguish between them; naming violates verb_noun clarity principle. Should be '{prefix}_execute_rest', '{prefix}_execute_graphql', '{prefix}_execute_sparql'.
Tool descriptions exceed 1000 characters (search: ~800, execute variants: ~600+), violating guideline max of 1024 and best practice of 50-200 chars. Verbose descriptions waste tokens and bury key details. Descriptions should state WHAT, WHEN, and RETURN in concise form.
Output schemas are not formally documented. Descriptions mention '__staged' field and staging behavior, but no structured return type schema is visible. LLMs cannot plan downstream calls without knowing response structure.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | F | 35 | 2026-07-28+ | v2 |
Search the API catalog (catalog mode) or OpenAPI spec (OpenAPI mode). Returns matching endpoints with full parameter docs. Use this to discover API capabilities before calling {prefix}_execute. Categories are listed. USAGE IN {prefix}_execute: api.get(path, params) for GET, api.post(path, body, params) for POST. Path params like /lookup/{id} are auto-interpolated from params. Remaining params become query string. Large responses (>100KB) are auto-staged: check result.__staged, return the staging info, use {prefix}_query_data to explore. Use limit/pagination params to control response size.
No error handling guidance. Descriptions do not explain what errors can occur, how to recover, or what the LLM should do if a call fails. Missing recovery patterns like 'If query fails, try simplifying the code' or 'If staging occurs, use {prefix}_query_data'.
Parameter 'code' in execute tools lacks constraints (min/max length, complexity limits). Unbounded code input invites LLMs to pass extremely long or malicious payloads. Should specify max length and document sandbox limitations.