Skip to content

feat: Add GraphQL MCP tools for schema retrieval and query execution#640

Merged
jfrench9 merged 3 commits intomainfrom
feature/graphql-mcp-tool
Apr 25, 2026
Merged

feat: Add GraphQL MCP tools for schema retrieval and query execution#640
jfrench9 merged 3 commits intomainfrom
feature/graphql-mcp-tool

Conversation

@jfrench9
Copy link
Copy Markdown
Member

Summary

Introduces a new GraphQL toolset for the MCP (Model Context Protocol) middleware layer, enabling AI agents to introspect GraphQL schemas and execute queries/mutations against configured GraphQL endpoints.

Key Accomplishments

  • New graphql_tool.py module: Implements comprehensive GraphQL capabilities including:

    • Schema introspection and retrieval, allowing agents to discover available types, queries, and mutations
    • GraphQL query and mutation execution with variable support
    • Proper error handling and response formatting for MCP consumption
  • Tool manager integration: Updated the MCP tools manager to register and expose the new GraphQL tools alongside existing tooling, ensuring seamless discovery by connected agents

  • Environment configuration: Added new environment variables to support GraphQL endpoint configuration, keeping credentials and endpoints externalized and secure

  • Documentation: Updated the MCP README with details on the new GraphQL tool capabilities and usage context

  • Comprehensive test coverage: Added 338+ lines of tests covering:

    • Schema retrieval (success and failure scenarios)
    • Query execution with variables
    • Error handling and edge cases
    • Tool registration and manager integration

Breaking Changes

None. This is a purely additive feature. Existing MCP tools and configurations remain unaffected.

Testing Notes

  • New unit tests are included in tests/middleware/mcp/tools/test_graphql_tool.py
  • Tests mock external GraphQL HTTP calls, so no live endpoint is required to run the suite
  • Verify that the new environment variables for GraphQL endpoint configuration are documented and available in deployment environments before enabling the tools in production

Infrastructure Considerations

  • New environment variables required: The GraphQL tools depend on endpoint URL and optional authentication configuration being set in the environment. Ensure these are provisioned in all target deployment environments (staging, production) prior to rollout.
  • Network access: The runtime environment must have outbound network access to the target GraphQL API endpoint(s).
  • No new dependencies expected beyond what is already available in the project's HTTP/networking stack, but verify that any GraphQL client libraries (if introduced) are captured in dependency manifests.

🤖 Generated with Claude Code

Branch Info:

  • Source: feature/graphql-mcp-tool
  • Target: main
  • Type: feature

Co-Authored-By: Claude noreply@anthropic.com

@jfrench9
Copy link
Copy Markdown
Member Author

@claude please review this PR

@claude
Copy link
Copy Markdown

claude Bot commented Apr 25, 2026

Claude finished @jfrench9's task in 2m 16s —— View job


PR Review: feat: Add GraphQL MCP tools for schema retrieval and query execution

Tasks:

  • Gather context and understand request
  • Read changed files
  • Analyze code for bugs, security, patterns, and best practices
  • Post comprehensive review feedback

Overall this is a well-structured addition that follows the existing MCP tool patterns correctly. The dual-flag kill-switch design (EXTENSIONS_GRAPHQL_ENABLED + MCP_GRAPHQL_ENABLED) is clean, the test coverage is solid, and the mutation/subscription rejection gate is authoritative. A few issues worth addressing before merge:


Security: Fragment Spread Complexity Bypass

_walk_complexity in graphql_tool.py:108-115 handles FragmentSpreadNode by counting zero fields for the spread itself, but it does not resolve the fragment definition to its inline selections. This means an agent can trivially bypass the field limit:

fragment A on Query { f1 f2 f3 ... f200 }
{ ...A ...A ...A ...A ...A }
# Counted as 5 fields. Actually executes 1000.

Since FragmentSpreadNode selection sets live in doc.definitions (not on the spread node itself), this requires a second pass or a fragment map. This should be fixed before enabling the tool in production.

Fix this →


Bug: None Introspection Data Cached as "null"

In graphql_tool.py:191-195:

if r.errors:
    logger.warning(...)
result = json.dumps(r.data)  # r.data could be None if errors occurred
_SCHEMA_CACHE[format_] = result  # caches "null" permanently

If the introspection query returns errors and r.data is None, json.dumps(None) = "null" gets cached for the process lifetime. A caller receiving {"schema": "null"} will be confused. Add a guard:

if r.errors or r.data is None:
    return {"error": "introspection_failed", "message": str(r.errors)}

Fix this →


Concern: Blocking DB Call Inside async execute()

_fetch_user (graphql_tool.py:314-330) calls next(db_gen) (sync) from inside async def execute(), which will block the event loop for the duration of the DB query. The same pattern is used in manager.py:_get_cached_meta (which is called from __init__, not an async context), so this appears to be an established trade-off — but calling it from an async hot path is different. Worth flagging for a follow-up: this should either use asyncio.to_thread() or an async session.


Minor: Hardcoded graph_type: "entity" in _build_context

graphql_tool.py:311: "graph_type": "entity" is hardcoded. If graph_type is used by any GraphQL resolver to make decisions (e.g., for shared repos), this will be incorrect. The client has no graph_type attribute today, but the context key exists. Either derive it from self.client or document that the GraphQL tool is intentionally scoped to entity graphs only and add a guard at construction time.


Minor: Redundant Exception Branches

graphql_tool.py:258-263:

try:
    doc = gql_parse(query)
except GraphQLSyntaxError as e:
    return {"error": "parse_error", "message": str(e)}
except Exception as e:
    return {"error": "parse_error", "message": str(e)}

Both branches return the same structure. Collapse to except (GraphQLSyntaxError, Exception) as e: or just except Exception as e: (since GraphQLSyntaxError is already an Exception).


Missing Test: Fragment Spread Complexity

No test covers the fragment-spread bypass scenario. Once the bypass is fixed, add a test like:

async def test_fragment_spread_field_limit(self, mock_client):
    # Fragment with 200 fields, spread 2× = 400 total, should reject
    fields = " ".join(f"f{i}" for i in range(200))
    query = f"fragment A on Query {{ {fields} }} {{ ...A ...A }}"
    result = await tool.execute({"query": query})
    assert result["error"] == "query_too_complex"

__init__.py Eagerly Imports Graphql Tool

tools/__init__.py:21: from .graphql_tool import GraphqlQueryTool, GraphqlSchemaTool is now a top-level import, pulling in graphql (graphql-core) and deferred schema references at module import time. The module-level gql_schema: Any = None defers the Strawberry schema correctly, but verify that importing graphql_tool at module level doesn't break cold-start behavior (e.g., during Dagster definitions loading where no Strawberry schema is wired). The lazy _ensure_gql_schema guards the runtime path, but the graphql library import itself must succeed at startup.


Env: MCP_GRAPHQL_ENABLED Defaults to true

env.py:580-583: MCP_GRAPHQL_ENABLED defaults true via SSM. This means any deployment with EXTENSIONS_GRAPHQL_ENABLED=true (also true by default) will expose the GraphQL MCP tools automatically. Ensure staging/production SSM parameters are set intentionally before this merges if the intent is a staged rollout.


Tests: Overall Quality

The test file is thorough and well-structured. Fixtures are clean, the autouse cache/schema resets are correct, and coverage of the complexity gates, operation rejection, and context threading is solid. Good work.

Comment thread tests/middleware/mcp/tools/test_graphql_tool.py Fixed
Comment thread tests/middleware/mcp/tools/test_graphql_tool.py Fixed
@jfrench9 jfrench9 merged commit 6fca825 into main Apr 25, 2026
7 checks passed
@jfrench9 jfrench9 deleted the feature/graphql-mcp-tool branch April 25, 2026 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant