Model Context Protocol server for executing code against multiple API backends (REST, GraphQL, SPARQL) with auto-staging, schema discovery, and sandboxed V8 isolate execution
The EIA MCP server exposes 4 tools with significant definition gaps. All tools have descriptions, but they are verbose (500+ chars) and lack clarity on when to use each variant. Input schemas are present but incomplete: the {prefix}_search tool has 3 parameters with types and descriptions, but the three {prefix}_execute variants (REST, GraphQL, SPARQL) accept only 'code' (string) and optional 'workspace', no constraints, enums, or guidance on valid code patterns. Output schemas are entirely undocumented. Error handling is absent from descriptions. The tools conflate multiple responsibilities (search + execute in one tool family) and lack idempotent/destructive hints. Naming is generic ('execute' appears 3 times for different APIs). No per-tool security or permission declarations.
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; 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.
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.
Three tools named '{prefix}_execute' with different APIs (REST, GraphQL, SPARQL). LLMs cannot distinguish them by name alone. Requires reading full descriptions to pick the right one, increasing selection errors.
Input schemas for all {prefix}_execute variants lack constraints. 'code' parameter accepts arbitrary JavaScript with no validation, format hints, or examples. No enum, pattern, or minLength/maxLength. LLMs cannot infer valid code structure.
No output schemas documented for any tool. LLMs do not know what fields to expect in responses, making downstream tool chaining and data extraction error-prone.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | F | 34 | 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.
Descriptions are 500+ characters and verbose. Baseline for A+ tools is 50 - 200 chars. Long descriptions waste tokens and bury key decision points. E.g., eia_execute (REST) description is ~800 chars with nested bullet points.
No error handling guidance in tool descriptions. If code execution fails, LLMs have no recovery path. No mention of retryable vs. fatal errors, or how to debug invalid code.