From 70b508facbc1850c60bc8374be7d84cec21e70d1 Mon Sep 17 00:00:00 2001 From: Boris Besemer Date: Mon, 8 Sep 2025 15:18:31 +0200 Subject: [PATCH 1/5] docs: add CLAUDE.md for AI-assisted development MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive documentation file to guide Claude Code and other AI assistants when working with this repository. Includes: - Essential development commands - High-level architecture overview - Core components and design patterns - Environment variables reference 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- CLAUDE.md | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2d334bc --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,92 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Commands + +### Development +- `bun --watch src/index.ts` - Run the MCP server in development mode with hot reload +- `bun build src/index.ts --outdir dist --target node` - Build the project for distribution +- `bun run format` - Format code using Biome (uses tabs with width 2) +- `bun run check` - Check code formatting without modifying files + +### Testing GraphQL Operations +When testing GraphQL file loading functionality: +1. Create `.graphql` or `.gql` files in the `graphql/` directory +2. Run the server with `GRAPHQL_DIR=./graphql bun --watch src/index.ts` +3. Operations are automatically registered as tools with names like `gql-{operation-name}` + +## Architecture + +### Core Components + +**MCP Server Implementation** (`src/index.ts`) +- Uses `@modelcontextprotocol/sdk` to implement a Model Context Protocol server +- Exposes GraphQL operations as MCP tools that LLMs can invoke +- Registers both static tools (`introspect-schema`, `query-graphql`) and dynamic tools from GraphQL files +- Environment-based configuration using Zod schema validation + +**GraphQL File Loading System** (`src/helpers/graphql-files.ts`) +- Recursively discovers `.graphql`/`.gql` files in configured directory +- Parses GraphQL operations and extracts variable definitions +- Converts GraphQL variable types to Zod schemas for runtime validation +- Generates tool names in kebab-case format (`gql-{operation-name}`) + +**Introspection Helpers** (`src/helpers/introspection.ts`) +- Supports three introspection modes: + 1. Direct endpoint introspection via GraphQL introspection query + 2. Local schema file parsing + 3. Remote schema file fetching from URL +- Returns schema in SDL (Schema Definition Language) format + +### Key Design Patterns + +**Environment Configuration** +- All configuration via environment variables (no CLI args as of v1.0.0) +- `ALLOW_MUTATIONS` defaults to `false` for safety +- `GRAPHQL_DIR` enables file-based operation loading +- Headers passed as JSON string in `HEADERS` env var + +**Tool Registration Flow** +1. On startup, scan `GRAPHQL_DIR` for GraphQL files +2. Parse each file to extract operations and variables +3. Skip mutations if `ALLOW_MUTATIONS=false` +4. Register each operation as an MCP tool with appropriate Zod schema +5. Tools execute by sending the full operation content to the endpoint + +**Security Model** +- Mutations disabled by default +- Same headers/endpoint used for all operations +- No dynamic query construction - only pre-defined operations from files + +## Development Guidelines + +### Adding New GraphQL Operations +Place `.graphql` files in the `graphql/` directory following this structure: +``` +graphql/ +├── queries/ # Read operations +└── mutations/ # Write operations (require ALLOW_MUTATIONS=true) +``` + +### Modifying Core Functionality +- Helper functions go in `src/helpers/` directory +- Use TypeScript strict mode (configured in tsconfig.json) +- Format with Biome using tabs (configured in biome.json) +- Package uses Bun as package manager (bun@1.2.19) + +### Dependencies +- **Runtime**: `@modelcontextprotocol/sdk`, `graphql`, `zod` +- **Development**: Uses Bun runtime, TypeScript, Biome formatter +- **GraphQL Tools**: `@graphql-tools/schema` for schema manipulation + +## Environment Variables Reference + +| Variable | Required | Default | Purpose | +|----------|----------|---------|---------| +| `ENDPOINT` | No | `http://localhost:4000/graphql` | GraphQL API endpoint | +| `HEADERS` | No | `{}` | JSON string of request headers | +| `ALLOW_MUTATIONS` | No | `false` | Enable mutation operations | +| `NAME` | No | `mcp-graphql` | MCP server name | +| `SCHEMA` | No | - | Local file path or URL to schema | +| `GRAPHQL_DIR` | No | `./graphql` | Directory for .graphql files | \ No newline at end of file From f432e454c9c04fb44743d1b268fa637c56cd36ac Mon Sep 17 00:00:00 2001 From: Boris Besemer Date: Mon, 8 Sep 2025 15:19:25 +0200 Subject: [PATCH 2/5] feat: add support for GraphQL operations from .graphql files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement dynamic tool generation from GraphQL operation files: - Auto-discover .graphql/.gql files in configurable directory - Parse operations and extract variable definitions - Generate MCP tools with proper Zod schema validation - Support query/mutation/subscription operations - Add GRAPHQL_DIR environment variable (defaults to ./graphql) - Include example GraphQL operation files Operations are registered as tools with pattern: gql-{operation-name} Mutations respect ALLOW_MUTATIONS setting for security 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- README.md | 56 ++++++++- graphql/mutations/createPost.graphql | 16 +++ graphql/mutations/updateUser.graphql | 11 ++ graphql/queries/getUser.graphql | 8 ++ graphql/queries/listPosts.graphql | 13 ++ graphql/queries/searchUsers.graphql | 12 ++ src/helpers/graphql-files.ts | 170 +++++++++++++++++++++++++++ src/index.ts | 108 +++++++++++++++++ 8 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 graphql/mutations/createPost.graphql create mode 100644 graphql/mutations/updateUser.graphql create mode 100644 graphql/queries/getUser.graphql create mode 100644 graphql/queries/listPosts.graphql create mode 100644 graphql/queries/searchUsers.graphql create mode 100644 src/helpers/graphql-files.ts diff --git a/README.md b/README.md index abe8972..e09b016 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Run `mcp-graphql` with the correct endpoint, it will automatically try to intros | `ALLOW_MUTATIONS` | Enable mutation operations (disabled by default) | `false` | | `NAME` | Name of the MCP server | `mcp-graphql` | | `SCHEMA` | Path to a local GraphQL schema file or URL (optional) | - | +| `GRAPHQL_DIR` | Directory containing .graphql/.gql files for operations | `./graphql` | ### Examples @@ -39,6 +40,9 @@ ENDPOINT=http://localhost:3000/graphql SCHEMA=./schema.graphql npx mcp-graphql # Using a schema file hosted at a URL ENDPOINT=http://localhost:3000/graphql SCHEMA=https://example.com/schema.graphql npx mcp-graphql + +# Using GraphQL files from a custom directory +ENDPOINT=http://localhost:3000/graphql GRAPHQL_DIR=./my-queries npx mcp-graphql ``` ## Resources @@ -47,13 +51,15 @@ ENDPOINT=http://localhost:3000/graphql SCHEMA=https://example.com/schema.graphql ## Available Tools -The server provides two main tools: +The server provides several tools: 1. **introspect-schema**: This tool retrieves the GraphQL schema. Use this first if you don't have access to the schema as a resource. This uses either the local schema file, a schema file hosted at a URL, or an introspection query. 2. **query-graphql**: Execute GraphQL queries against the endpoint. By default, mutations are disabled unless `ALLOW_MUTATIONS` is set to `true`. +3. **Dynamic tools from .graphql files**: Any GraphQL operations defined in `.graphql` or `.gql` files within the `GRAPHQL_DIR` directory are automatically registered as MCP tools. Tool names follow the pattern `gql-{operation-name}` (e.g., `gql-get-user`, `gql-create-post`). + ## Installation ### Installing via Smithery @@ -81,10 +87,58 @@ It can be manually installed to Claude: } ``` +## GraphQL File Support + +You can define GraphQL operations in `.graphql` or `.gql` files, and they will be automatically registered as MCP tools. This allows you to: + +- Version control your GraphQL operations +- Organize operations by type or domain +- Reuse common queries across different contexts +- Type-check operations against your schema + +### File Structure Example + +``` +graphql/ +├── queries/ +│ ├── getUser.graphql +│ ├── listPosts.graphql +│ └── searchUsers.graphql +└── mutations/ + ├── createPost.graphql + └── updateUser.graphql +``` + +### Operation File Example + +```graphql +# getUser.graphql +query GetUser($id: ID!) { + user(id: $id) { + id + name + email + createdAt + } +} +``` + +This operation will be available as the `gql-get-user` tool, accepting an `id` parameter. + +### Naming Conventions + +- Operations with explicit names use that name (e.g., `query GetUser` → `gql-get-user`) +- Operations without names use the filename (e.g., `userProfile.graphql` → `gql-user-profile`) +- Names are converted to kebab-case for consistency + ## Security Considerations Mutations are disabled by default as a security measure to prevent an LLM from modifying your database or service data. Consider carefully before enabling mutations in production environments. +When using GraphQL files: +- Mutation operations in `.graphql` files are skipped unless `ALLOW_MUTATIONS=true` +- Each operation is executed with the same headers and endpoint configuration + ## Customize for your own server This is a very generic implementation where it allows for complete introspection and for your users to do whatever (including mutations). If you need a more specific implementation I'd suggest to just create your own MCP and lock down tool calling for clients to only input specific query fields and/or variables. You can use this as a reference. diff --git a/graphql/mutations/createPost.graphql b/graphql/mutations/createPost.graphql new file mode 100644 index 0000000..c484b54 --- /dev/null +++ b/graphql/mutations/createPost.graphql @@ -0,0 +1,16 @@ +mutation CreatePost($title: String!, $content: String!, $authorId: ID!) { + createPost(input: { + title: $title, + content: $content, + authorId: $authorId + }) { + id + title + content + author { + id + name + } + createdAt + } +} \ No newline at end of file diff --git a/graphql/mutations/updateUser.graphql b/graphql/mutations/updateUser.graphql new file mode 100644 index 0000000..fc31a29 --- /dev/null +++ b/graphql/mutations/updateUser.graphql @@ -0,0 +1,11 @@ +mutation UpdateUser($id: ID!, $name: String, $email: String) { + updateUser(id: $id, input: { + name: $name, + email: $email + }) { + id + name + email + updatedAt + } +} \ No newline at end of file diff --git a/graphql/queries/getUser.graphql b/graphql/queries/getUser.graphql new file mode 100644 index 0000000..70ff5b3 --- /dev/null +++ b/graphql/queries/getUser.graphql @@ -0,0 +1,8 @@ +query GetUser($id: ID!) { + user(id: $id) { + id + name + email + createdAt + } +} \ No newline at end of file diff --git a/graphql/queries/listPosts.graphql b/graphql/queries/listPosts.graphql new file mode 100644 index 0000000..d410c14 --- /dev/null +++ b/graphql/queries/listPosts.graphql @@ -0,0 +1,13 @@ +query ListPosts($limit: Int = 10, $offset: Int = 0) { + posts(limit: $limit, offset: $offset) { + id + title + content + author { + id + name + } + createdAt + updatedAt + } +} \ No newline at end of file diff --git a/graphql/queries/searchUsers.graphql b/graphql/queries/searchUsers.graphql new file mode 100644 index 0000000..abe0972 --- /dev/null +++ b/graphql/queries/searchUsers.graphql @@ -0,0 +1,12 @@ +query SearchUsers($query: String!, $filters: UserFilters) { + searchUsers(query: $query, filters: $filters) { + totalCount + users { + id + name + email + role + isActive + } + } +} \ No newline at end of file diff --git a/src/helpers/graphql-files.ts b/src/helpers/graphql-files.ts new file mode 100644 index 0000000..57fe8bf --- /dev/null +++ b/src/helpers/graphql-files.ts @@ -0,0 +1,170 @@ +import { readdir, readFile } from "node:fs/promises"; +import { join, basename, extname } from "node:path"; +import { existsSync } from "node:fs"; +import { + parse, + type DocumentNode, + type OperationDefinitionNode, + type VariableDefinitionNode, +} from "graphql"; +import { z } from "zod"; + +export interface GraphQLOperation { + name: string; + type: "query" | "mutation" | "subscription"; + content: string; + variables: VariableDefinitionNode[]; + filePath: string; +} + +export async function discoverGraphQLFiles( + directory: string, +): Promise { + if (!existsSync(directory)) { + return []; + } + + const files: string[] = []; + + async function scanDirectory(dir: string): Promise { + const entries = await readdir(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = join(dir, entry.name); + + if (entry.isDirectory()) { + await scanDirectory(fullPath); + } else if ( + entry.isFile() && + (entry.name.endsWith(".graphql") || entry.name.endsWith(".gql")) + ) { + files.push(fullPath); + } + } + } + + await scanDirectory(directory); + return files; +} + +export async function parseGraphQLFile( + filePath: string, +): Promise { + const content = await readFile(filePath, "utf-8"); + const operations: GraphQLOperation[] = []; + + try { + const document: DocumentNode = parse(content); + + for (const definition of document.definitions) { + if (definition.kind === "OperationDefinition") { + const operationDef = definition as OperationDefinitionNode; + + // Use operation name or derive from filename + const operationName = + operationDef.name?.value || + basename(filePath, extname(filePath)); + + operations.push({ + name: operationName, + type: operationDef.operation, + content: content, + variables: operationDef.variableDefinitions || [], + filePath, + }); + } + } + } catch (error) { + console.error(`Failed to parse GraphQL file ${filePath}:`, error); + } + + return operations; +} + +export async function loadAllGraphQLOperations( + directory: string, +): Promise { + const files = await discoverGraphQLFiles(directory); + const allOperations: GraphQLOperation[] = []; + + for (const file of files) { + const operations = await parseGraphQLFile(file); + allOperations.push(...operations); + } + + return allOperations; +} + +function getGraphQLTypeAsZod(type: any): z.ZodTypeAny { + // Handle NonNull types + if (type.kind === "NonNullType") { + return getGraphQLTypeAsZod(type.type); + } + + // Handle List types + if (type.kind === "ListType") { + return z.array(getGraphQLTypeAsZod(type.type)).optional(); + } + + // Handle Named types + if (type.kind === "NamedType") { + switch (type.name.value) { + case "String": + return z.string(); + case "Int": + return z.number().int(); + case "Float": + return z.number(); + case "Boolean": + return z.boolean(); + case "ID": + return z.string(); + default: + // For custom types, treat as JSON string + return z.string().describe(`GraphQL ${type.name.value} type`); + } + } + + // Default to string for unknown types + return z.string(); +} + +export function createVariableSchema( + variables: VariableDefinitionNode[], +): z.ZodObject { + const schemaShape: Record = {}; + + for (const variable of variables) { + const varName = variable.variable.name.value; + let zodType = getGraphQLTypeAsZod(variable.type); + + // Add default value if present + if (variable.defaultValue) { + zodType = zodType.optional(); + } + + // Check if the variable type is NonNull (required) + if (variable.type.kind !== "NonNullType") { + zodType = zodType.optional(); + } + + schemaShape[varName] = zodType; + } + + return z.object(schemaShape); +} + +export function generateToolName(operation: GraphQLOperation): string { + // Convert operation name to kebab-case for consistency + const kebabName = operation.name + .replace(/([A-Z])/g, "-$1") + .toLowerCase() + .replace(/^-/, ""); + + return `gql-${kebabName}`; +} + +export function generateToolDescription(operation: GraphQLOperation): string { + const operationType = operation.type.charAt(0).toUpperCase() + operation.type.slice(1); + return `${operationType} GraphQL operation: ${operation.name}`; +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 5118f0e..ef09c61 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,13 @@ import { introspectSchemaFromUrl, } from "./helpers/introspection.js"; import { getVersion } from "./helpers/package.js" with { type: "macro" }; +import { + loadAllGraphQLOperations, + createVariableSchema, + generateToolName, + generateToolDescription, + type GraphQLOperation, +} from "./helpers/graphql-files.js"; // Check for deprecated command line arguments checkDeprecatedArguments(); @@ -33,6 +40,7 @@ const EnvSchema = z.object({ } }), SCHEMA: z.string().optional(), + GRAPHQL_DIR: z.string().default("./graphql"), }); const env = EnvSchema.parse(process.env); @@ -214,7 +222,107 @@ server.tool( }, ); +async function registerGraphQLFileTools() { + const operations = await loadAllGraphQLOperations(env.GRAPHQL_DIR); + + if (operations.length > 0) { + console.error( + `Found ${operations.length} GraphQL operations in ${env.GRAPHQL_DIR}`, + ); + } + + for (const operation of operations) { + // Skip mutations if not allowed + if (operation.type === "mutation" && !env.ALLOW_MUTATIONS) { + console.error( + `Skipping mutation ${operation.name} (mutations disabled)`, + ); + continue; + } + + const toolName = generateToolName(operation); + const toolDescription = generateToolDescription(operation); + const variableSchema = createVariableSchema(operation.variables); + + server.tool( + toolName, + toolDescription, + variableSchema.shape, + async (variables) => { + try { + const response = await fetch(env.ENDPOINT, { + method: "POST", + headers: { + "Content-Type": "application/json", + ...env.HEADERS, + }, + body: JSON.stringify({ + query: operation.content, + variables, + }), + }); + + if (!response.ok) { + const responseText = await response.text(); + return { + isError: true, + content: [ + { + type: "text", + text: `GraphQL request failed: ${response.statusText}\n${responseText}`, + }, + ], + }; + } + + const data = await response.json(); + + if (data.errors && data.errors.length > 0) { + return { + isError: true, + content: [ + { + type: "text", + text: `GraphQL errors: ${JSON.stringify( + data, + null, + 2, + )}`, + }, + ], + }; + } + + return { + content: [ + { + type: "text", + text: JSON.stringify(data, null, 2), + }, + ], + }; + } catch (error) { + return { + isError: true, + content: [ + { + type: "text", + text: `Failed to execute GraphQL operation: ${error}`, + }, + ], + }; + } + }, + ); + + console.error(`Registered tool: ${toolName}`); + } +} + async function main() { + // Register tools from GraphQL files + await registerGraphQLFileTools(); + const transport = new StdioServerTransport(); await server.connect(transport); From ce930ed8d24bef20a1dd818baf6243ddc3aa592a Mon Sep 17 00:00:00 2001 From: Boris Besemer Date: Mon, 8 Sep 2025 15:30:49 +0200 Subject: [PATCH 3/5] feat: add GraphQL Config support for operation loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace manual file discovery with standard GraphQL Config format: - Support .graphqlrc.{yml,json} and graphql.config.js files - Use 'documents' field with glob patterns for operation discovery - Add multi-project support with namespaced tool names - Map config fields to existing environment variables for compatibility - Maintain backward compatibility with GRAPHQL_DIR env var - Include example config files in multiple formats Benefits: - Standard format used by GraphQL ecosystem tools - Better IDE integration and validation capabilities - Environment variable interpolation support - Simplified configuration management Original introspect-schema and query-graphql tools remain unchanged. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .graphqlrc.example.json | 18 +++ .graphqlrc.yml | 52 +++++++ README.md | 98 +++++++++++++- bun.lock | 223 +++++++++++++++++++++++++++++- graphql.config.example.js | 79 +++++++++++ package.json | 1 + src/helpers/graphql-config.ts | 247 ++++++++++++++++++++++++++++++++++ src/index.ts | 34 +++-- 8 files changed, 735 insertions(+), 17 deletions(-) create mode 100644 .graphqlrc.example.json create mode 100644 .graphqlrc.yml create mode 100644 graphql.config.example.js create mode 100644 src/helpers/graphql-config.ts diff --git a/.graphqlrc.example.json b/.graphqlrc.example.json new file mode 100644 index 0000000..8322ec1 --- /dev/null +++ b/.graphqlrc.example.json @@ -0,0 +1,18 @@ +{ + "schema": "http://localhost:4000/graphql", + "documents": [ + "./graphql/**/*.graphql", + "./graphql/**/*.gql" + ], + "extensions": { + "endpoints": { + "default": { + "url": "http://localhost:4000/graphql", + "headers": { + "Authorization": "Bearer ${API_TOKEN}", + "Content-Type": "application/json" + } + } + } + } +} \ No newline at end of file diff --git a/.graphqlrc.yml b/.graphqlrc.yml new file mode 100644 index 0000000..07ca027 --- /dev/null +++ b/.graphqlrc.yml @@ -0,0 +1,52 @@ +# GraphQL Config for mcp-graphql +# This file configures GraphQL operations and schema for the MCP server + +# Schema location - can be: +# - Local file: ./schema.graphql +# - URL: https://api.example.com/graphql +# - Introspection endpoint: http://localhost:4000/graphql +schema: ${SCHEMA:-http://localhost:4000/graphql} + +# Documents - GraphQL operations to load as MCP tools +# Supports glob patterns +documents: + - './graphql/**/*.graphql' + - './graphql/**/*.gql' + - './operations/**/*.graphql' + +# Extensions for additional configuration +extensions: + # Endpoint configuration + endpoints: + default: + # GraphQL endpoint URL + url: ${ENDPOINT:-http://localhost:4000/graphql} + # Headers for requests (supports environment variables) + headers: + Authorization: Bearer ${API_TOKEN} + Content-Type: application/json + +# Multi-project example (commented out) +# Uncomment to use multiple projects with different configurations +# projects: +# # Production API +# production: +# schema: https://api.example.com/graphql +# documents: './graphql/production/**/*.graphql' +# extensions: +# endpoints: +# default: +# url: https://api.example.com/graphql +# headers: +# Authorization: Bearer ${PROD_TOKEN} +# +# # Development API +# development: +# schema: http://localhost:4000/graphql +# documents: './graphql/dev/**/*.graphql' +# extensions: +# endpoints: +# default: +# url: http://localhost:4000/graphql +# headers: +# Authorization: Bearer ${DEV_TOKEN} \ No newline at end of file diff --git a/README.md b/README.md index e09b016..a61ba9e 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,42 @@ A Model Context Protocol server that enables LLMs to interact with GraphQL APIs. ## Usage -Run `mcp-graphql` with the correct endpoint, it will automatically try to introspect your queries. +Run `mcp-graphql` with either a GraphQL Config file or environment variables. The server will automatically load your GraphQL operations as MCP tools. -### Environment Variables (Breaking change in 1.0.0) +### Configuration Methods -> **Note:** As of version 1.0.0, command line arguments have been replaced with environment variables. +The server supports two configuration methods (in priority order): + +1. **GraphQL Config File** (Recommended) - Standard `.graphqlrc.yml`, `.graphqlrc.json`, or `graphql.config.js` +2. **Environment Variables** - For backward compatibility and simple setups + +### GraphQL Config (Recommended) + +Create a `.graphqlrc.yml` file in your project root: + +```yaml +schema: http://localhost:4000/graphql +documents: + - './graphql/**/*.graphql' + - './operations/**/*.gql' +extensions: + endpoints: + default: + url: http://localhost:4000/graphql + headers: + Authorization: Bearer ${API_TOKEN} +``` + +Supported config formats: +- `.graphqlrc.yml` / `.graphqlrc.yaml` +- `.graphqlrc.json` +- `graphql.config.js` / `graphql.config.ts` +- `.graphqlrc` (JSON or YAML) +- `package.json` (under `graphql` key) + +### Environment Variables + +> **Note:** Environment variables are used as fallbacks when GraphQL Config is not present. | Environment Variable | Description | Default | |----------|-------------|---------| @@ -41,8 +72,11 @@ ENDPOINT=http://localhost:3000/graphql SCHEMA=./schema.graphql npx mcp-graphql # Using a schema file hosted at a URL ENDPOINT=http://localhost:3000/graphql SCHEMA=https://example.com/schema.graphql npx mcp-graphql -# Using GraphQL files from a custom directory +# Using GraphQL files from a custom directory (without config file) ENDPOINT=http://localhost:3000/graphql GRAPHQL_DIR=./my-queries npx mcp-graphql + +# Using GraphQL Config file (recommended) +npx mcp-graphql # Automatically loads .graphqlrc.yml or other config formats ``` ## Resources @@ -60,6 +94,19 @@ This uses either the local schema file, a schema file hosted at a URL, or an int 3. **Dynamic tools from .graphql files**: Any GraphQL operations defined in `.graphql` or `.gql` files within the `GRAPHQL_DIR` directory are automatically registered as MCP tools. Tool names follow the pattern `gql-{operation-name}` (e.g., `gql-get-user`, `gql-create-post`). +## Migration from Environment Variables + +To migrate from environment variables to GraphQL Config: + +1. Create a `.graphqlrc.yml` file +2. Map your environment variables: + - `ENDPOINT` → `extensions.endpoints.default.url` + - `HEADERS` → `extensions.endpoints.default.headers` + - `SCHEMA` → `schema` + - `GRAPHQL_DIR` → `documents` (as glob patterns) +3. Remove `GRAPHQL_DIR` from your environment +4. Keep other env vars as fallbacks or use `${ENV_VAR}` in config + ## Installation ### Installing via Smithery @@ -87,15 +134,51 @@ It can be manually installed to Claude: } ``` -## GraphQL File Support +## GraphQL Config Support + +The server now supports [GraphQL Config](https://graphql-config.com/), the standard configuration format used by GraphQL tools. This provides: -You can define GraphQL operations in `.graphql` or `.gql` files, and they will be automatically registered as MCP tools. This allows you to: +- **Standard format**: Works with existing GraphQL tooling (VSCode, GraphQL Playground, etc.) +- **Documents field**: Define operation files using glob patterns +- **Multi-project support**: Different configurations for different environments +- **Environment interpolation**: Use `${ENV_VAR}` in config values +- **Schema validation**: Validate operations against your schema + +### Benefits of GraphQL Config - Version control your GraphQL operations +- Share configuration across tools (IDEs, linters, code generators) - Organize operations by type or domain -- Reuse common queries across different contexts - Type-check operations against your schema +### Multi-Project Configuration + +Support multiple GraphQL APIs in a single configuration: + +```yaml +# .graphqlrc.yml +projects: + production: + schema: https://api.example.com/graphql + documents: './graphql/production/**/*.graphql' + extensions: + endpoints: + default: + url: https://api.example.com/graphql + headers: + Authorization: Bearer ${PROD_TOKEN} + + development: + schema: http://localhost:4000/graphql + documents: './graphql/dev/**/*.graphql' + extensions: + endpoints: + default: + url: http://localhost:4000/graphql +``` + +Tools from multi-project configs are named: `gql-{project}-{operation}` + ### File Structure Example ``` @@ -129,6 +212,7 @@ This operation will be available as the `gql-get-user` tool, accepting an `id` p - Operations with explicit names use that name (e.g., `query GetUser` → `gql-get-user`) - Operations without names use the filename (e.g., `userProfile.graphql` → `gql-user-profile`) +- Multi-project operations include project name (e.g., `gql-production-get-user`) - Names are converted to kebab-case for consistency ## Security Considerations diff --git a/bun.lock b/bun.lock index d54066d..770ffdc 100644 --- a/bun.lock +++ b/bun.lock @@ -6,6 +6,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "1.12.0", "graphql": "^16.11.0", + "graphql-config": "^5.1.5", "yargs": "17.7.2", "zod": "3.25.30", "zod-to-json-schema": "3.24.5", @@ -21,6 +22,10 @@ }, }, "packages": { + "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], + "@envelop/core": ["@envelop/core@5.2.3", "", { "dependencies": { "@envelop/instrumentation": "^1.0.0", "@envelop/types": "^5.2.1", "@whatwg-node/promise-helpers": "^1.2.4", "tslib": "^2.5.0" } }, "sha512-KfoGlYD/XXQSc3BkM1/k15+JQbkQ4ateHazeZoWl9P71FsLTDXSjGy6j7QqfhpIDSbxNISqhPMfZHYSbDFOofQ=="], "@envelop/instrumentation": ["@envelop/instrumentation@1.0.0", "", { "dependencies": { "@whatwg-node/promise-helpers": "^1.2.1", "tslib": "^2.5.0" } }, "sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw=="], @@ -29,14 +34,40 @@ "@fastify/busboy": ["@fastify/busboy@3.1.1", "", {}, "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw=="], + "@graphql-hive/signal": ["@graphql-hive/signal@1.0.0", "", {}, "sha512-RiwLMc89lTjvyLEivZ/qxAC5nBHoS2CtsWFSOsN35sxG9zoo5Z+JsFHM8MlvmO9yt+MJNIyC5MLE1rsbOphlag=="], + + "@graphql-tools/batch-execute": ["@graphql-tools/batch-execute@9.0.19", "", { "dependencies": { "@graphql-tools/utils": "^10.9.1", "@whatwg-node/promise-helpers": "^1.3.0", "dataloader": "^2.2.3", "tslib": "^2.8.1" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-VGamgY4PLzSx48IHPoblRw0oTaBa7S26RpZXt0Y4NN90ytoE0LutlpB2484RbkfcTjv9wa64QD474+YP1kEgGA=="], + + "@graphql-tools/delegate": ["@graphql-tools/delegate@10.2.23", "", { "dependencies": { "@graphql-tools/batch-execute": "^9.0.19", "@graphql-tools/executor": "^1.4.9", "@graphql-tools/schema": "^10.0.25", "@graphql-tools/utils": "^10.9.1", "@repeaterjs/repeater": "^3.0.6", "@whatwg-node/promise-helpers": "^1.3.0", "dataloader": "^2.2.3", "dset": "^3.1.2", "tslib": "^2.8.1" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-xrPtl7f1LxS+B6o+W7ueuQh67CwRkfl+UKJncaslnqYdkxKmNBB4wnzVcW8ZsRdwbsla/v43PtwAvSlzxCzq2w=="], + "@graphql-tools/executor": ["@graphql-tools/executor@1.4.7", "", { "dependencies": { "@graphql-tools/utils": "^10.8.6", "@graphql-typed-document-node/core": "^3.2.0", "@repeaterjs/repeater": "^3.0.4", "@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/promise-helpers": "^1.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-U0nK9jzJRP9/9Izf1+0Gggd6K6RNRsheFo1gC/VWzfnsr0qjcOSS9qTjY0OTC5iTPt4tQ+W5Zpw/uc7mebI6aA=="], + "@graphql-tools/executor-common": ["@graphql-tools/executor-common@0.0.6", "", { "dependencies": { "@envelop/core": "^5.3.0", "@graphql-tools/utils": "^10.9.1" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-JAH/R1zf77CSkpYATIJw+eOJwsbWocdDjY+avY7G+P5HCXxwQjAjWVkJI1QJBQYjPQDVxwf1fmTZlIN3VOadow=="], + + "@graphql-tools/executor-graphql-ws": ["@graphql-tools/executor-graphql-ws@2.0.7", "", { "dependencies": { "@graphql-tools/executor-common": "^0.0.6", "@graphql-tools/utils": "^10.9.1", "@whatwg-node/disposablestack": "^0.0.6", "graphql-ws": "^6.0.6", "isomorphic-ws": "^5.0.0", "tslib": "^2.8.1", "ws": "^8.18.3" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-J27za7sKF6RjhmvSOwOQFeNhNHyP4f4niqPnerJmq73OtLx9Y2PGOhkXOEB0PjhvPJceuttkD2O1yMgEkTGs3Q=="], + + "@graphql-tools/executor-http": ["@graphql-tools/executor-http@1.3.3", "", { "dependencies": { "@graphql-hive/signal": "^1.0.0", "@graphql-tools/executor-common": "^0.0.4", "@graphql-tools/utils": "^10.8.1", "@repeaterjs/repeater": "^3.0.4", "@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/fetch": "^0.10.4", "@whatwg-node/promise-helpers": "^1.3.0", "meros": "^1.2.1", "tslib": "^2.8.1" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-LIy+l08/Ivl8f8sMiHW2ebyck59JzyzO/yF9SFS4NH6MJZUezA1xThUXCDIKhHiD56h/gPojbkpcFvM2CbNE7A=="], + + "@graphql-tools/executor-legacy-ws": ["@graphql-tools/executor-legacy-ws@1.1.19", "", { "dependencies": { "@graphql-tools/utils": "^10.9.1", "@types/ws": "^8.0.0", "isomorphic-ws": "^5.0.0", "tslib": "^2.4.0", "ws": "^8.17.1" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-bEbv/SlEdhWQD0WZLUX1kOenEdVZk1yYtilrAWjRUgfHRZoEkY9s+oiqOxnth3z68wC2MWYx7ykkS5hhDamixg=="], + + "@graphql-tools/graphql-file-loader": ["@graphql-tools/graphql-file-loader@8.1.1", "", { "dependencies": { "@graphql-tools/import": "7.1.1", "@graphql-tools/utils": "^10.9.1", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-5JaUE3zMHW21Oh3bGSNKcr/Mi6oZ9/QWlBCNYbGy+09U23EOZmhPn9a44zP3gXcnnj0C+YVEr8dsMaoaB3UVGQ=="], + + "@graphql-tools/import": ["@graphql-tools/import@7.1.1", "", { "dependencies": { "@graphql-tools/utils": "^10.9.1", "@theguild/federation-composition": "^0.19.0", "resolve-from": "5.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-zhlhaUmeTfV76vMoLRn9xCVMVc7sLf10ve5GKEhXFFDcWA6+vEZGk9CCm1VlPf2kyKGlF7bwLVzfepb3ZoOU9Q=="], + + "@graphql-tools/json-file-loader": ["@graphql-tools/json-file-loader@8.0.20", "", { "dependencies": { "@graphql-tools/utils": "^10.9.1", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-5v6W+ZLBBML5SgntuBDLsYoqUvwfNboAwL6BwPHi3z/hH1f8BS9/0+MCW9OGY712g7E4pc3y9KqS67mWF753eA=="], + + "@graphql-tools/load": ["@graphql-tools/load@8.1.2", "", { "dependencies": { "@graphql-tools/schema": "^10.0.25", "@graphql-tools/utils": "^10.9.1", "p-limit": "3.1.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-WhDPv25/jRND+0uripofMX0IEwo6mrv+tJg6HifRmDu8USCD7nZhufT0PP7lIcuutqjIQFyogqT70BQsy6wOgw=="], + "@graphql-tools/merge": ["@graphql-tools/merge@9.0.24", "", { "dependencies": { "@graphql-tools/utils": "^10.8.6", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-NzWx/Afl/1qHT3Nm1bghGG2l4jub28AdvtG11PoUlmjcIjnFBJMv4vqL0qnxWe8A82peWo4/TkVdjJRLXwgGEw=="], "@graphql-tools/schema": ["@graphql-tools/schema@10.0.23", "", { "dependencies": { "@graphql-tools/merge": "^9.0.24", "@graphql-tools/utils": "^10.8.6", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-aEGVpd1PCuGEwqTXCStpEkmheTHNdMayiIKH1xDWqYp9i8yKv9FRDgkGrY4RD8TNxnf7iII+6KOBGaJ3ygH95A=="], + "@graphql-tools/url-loader": ["@graphql-tools/url-loader@8.0.33", "", { "dependencies": { "@graphql-tools/executor-graphql-ws": "^2.0.1", "@graphql-tools/executor-http": "^1.1.9", "@graphql-tools/executor-legacy-ws": "^1.1.19", "@graphql-tools/utils": "^10.9.1", "@graphql-tools/wrap": "^10.0.16", "@types/ws": "^8.0.0", "@whatwg-node/fetch": "^0.10.0", "@whatwg-node/promise-helpers": "^1.0.0", "isomorphic-ws": "^5.0.0", "sync-fetch": "0.6.0-2", "tslib": "^2.4.0", "ws": "^8.17.1" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-Fu626qcNHcqAj8uYd7QRarcJn5XZ863kmxsg1sm0fyjyfBJnsvC7ddFt6Hayz5kxVKfsnjxiDfPMXanvsQVBKw=="], + "@graphql-tools/utils": ["@graphql-tools/utils@10.8.6", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-Alc9Vyg0oOsGhRapfL3xvqh1zV8nKoFUdtLhXX7Ki4nClaIJXckrA86j+uxEuG3ic6j4jlM1nvcWXRn/71AVLQ=="], + "@graphql-tools/wrap": ["@graphql-tools/wrap@10.1.4", "", { "dependencies": { "@graphql-tools/delegate": "^10.2.23", "@graphql-tools/schema": "^10.0.25", "@graphql-tools/utils": "^10.9.1", "@whatwg-node/promise-helpers": "^1.3.0", "tslib": "^2.8.1" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-7pyNKqXProRjlSdqOtrbnFRMQAVamCmEREilOXtZujxY6kYit3tvWWSjUrcIOheltTffoRh7EQSjpy2JDCzasg=="], + "@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="], "@graphql-yoga/logger": ["@graphql-yoga/logger@2.0.1", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-Nv0BoDGLMg9QBKy9cIswQ3/6aKaKjlTh87x3GiBg2Z4RrjyrM48DvOOK0pJh1C1At+b0mUIM67cwZcFTDLN4sA=="], @@ -47,12 +78,22 @@ "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.12.0", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-m//7RlINx1F3sz3KqwY1WWzVgTcYX52HYk4bJ1hkBXV3zccAEth+jRvG8DBRrdaQuRsPAJOx2MH3zaHNCKL7Zg=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + "@repeaterjs/repeater": ["@repeaterjs/repeater@3.0.6", "", {}, "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA=="], + "@theguild/federation-composition": ["@theguild/federation-composition@0.19.1", "", { "dependencies": { "constant-case": "^3.0.4", "debug": "4.4.1", "json5": "^2.2.3", "lodash.sortby": "^4.7.0" }, "peerDependencies": { "graphql": "^16.0.0" } }, "sha512-E4kllHSRYh+FsY0VR+fwl0rmWhDV8xUgWawLZTXmy15nCWQwj0BDsoEpdEXjPh7xes+75cRaeJcSbZ4jkBuSdg=="], + "@types/bun": ["@types/bun@1.2.14", "", { "dependencies": { "bun-types": "1.2.14" } }, "sha512-VsFZKs8oKHzI7zwvECiAJ5oSorWndIWEVhfbYqZd4HI/45kzW7PN2Rr5biAzvGvRuNmYLSANY+H59ubHq8xw7Q=="], "@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="], + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + "@types/yargs": ["@types/yargs@17.0.33", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA=="], "@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="], @@ -81,6 +122,10 @@ "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], + "asn1": ["asn1@0.2.6", "", { "dependencies": { "safer-buffer": "~2.1.0" } }, "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ=="], "assert-plus": ["assert-plus@1.0.0", "", {}, "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="], @@ -101,7 +146,9 @@ "body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], - "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], "bun-types": ["bun-types@1.2.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-Kuh4Ub28ucMRWeiUUWMHsT9Wcbr4H3kLIO72RZZElSDxSu7vpetRvxIUDUaW6QtaIeixIpm7OXtNnZPf82EzwA=="], @@ -111,6 +158,8 @@ "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + "caseless": ["caseless@0.12.0", "", {}, "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="], "chalk": ["chalk@1.1.3", "", { "dependencies": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" } }, "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A=="], @@ -133,6 +182,8 @@ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + "constant-case": ["constant-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", "upper-case": "^2.0.2" } }, "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ=="], + "content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="], "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], @@ -147,18 +198,26 @@ "cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="], + "cosmiconfig": ["cosmiconfig@8.3.6", "", { "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0", "path-type": "^4.0.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA=="], + "cross-inspect": ["cross-inspect@1.0.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "dashdash": ["dashdash@1.14.1", "", { "dependencies": { "assert-plus": "^1.0.0" } }, "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g=="], + "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], + + "dataloader": ["dataloader@2.2.3", "", {}, "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA=="], + "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], + "dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="], "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], @@ -175,6 +234,8 @@ "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "error-ex": ["error-ex@1.3.3", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-qp/sQFEMyluBQXosPGtY4ItAvSvrZf5SnAebwj+hjvYpZPWfciAZ+GB5JW4l/eunX675/rUmCfsEzoMch39ypA=="], + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], @@ -205,16 +266,26 @@ "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], + "figures": ["figures@1.7.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5", "object-assign": "^4.1.0" } }, "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ=="], + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "finalhandler": ["finalhandler@2.1.0", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q=="], "forever-agent": ["forever-agent@0.6.1", "", {}, "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw=="], "form-data": ["form-data@2.3.3", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", "mime-types": "^2.1.12" } }, "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ=="], + "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], @@ -237,12 +308,20 @@ "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "graphql": ["graphql@16.11.0", "", {}, "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw=="], + "graphql-config": ["graphql-config@5.1.5", "", { "dependencies": { "@graphql-tools/graphql-file-loader": "^8.0.0", "@graphql-tools/json-file-loader": "^8.0.0", "@graphql-tools/load": "^8.1.0", "@graphql-tools/merge": "^9.0.0", "@graphql-tools/url-loader": "^8.0.0", "@graphql-tools/utils": "^10.0.0", "cosmiconfig": "^8.1.0", "jiti": "^2.0.0", "minimatch": "^9.0.5", "string-env-interpolation": "^1.0.1", "tslib": "^2.4.0" }, "peerDependencies": { "cosmiconfig-toml-loader": "^1.0.0", "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" }, "optionalPeers": ["cosmiconfig-toml-loader"] }, "sha512-mG2LL1HccpU8qg5ajLROgdsBzx/o2M6kgI3uAmoaXiSH9PCUbtIyLomLqUtCFaAeG2YCFsl0M5cfQ9rKmDoMVA=="], + + "graphql-ws": ["graphql-ws@6.0.6", "", { "peerDependencies": { "@fastify/websocket": "^10 || ^11", "crossws": "~0.3", "graphql": "^15.10.1 || ^16", "uWebSockets.js": "^20", "ws": "^8" }, "optionalPeers": ["@fastify/websocket", "crossws", "uWebSockets.js", "ws"] }, "sha512-zgfER9s+ftkGKUZgc0xbx8T7/HMO4AV5/YuYiFc+AtgcO5T0v8AxYYNQ+ltzuzDZgNkYJaFspm5MMYLjQzrkmw=="], + "graphql-yoga": ["graphql-yoga@5.13.5", "", { "dependencies": { "@envelop/core": "^5.2.3", "@envelop/instrumentation": "^1.0.0", "@graphql-tools/executor": "^1.4.0", "@graphql-tools/schema": "^10.0.11", "@graphql-tools/utils": "^10.6.2", "@graphql-yoga/logger": "^2.0.1", "@graphql-yoga/subscription": "^5.0.5", "@whatwg-node/fetch": "^0.10.6", "@whatwg-node/promise-helpers": "^1.2.4", "@whatwg-node/server": "^0.10.5", "dset": "^3.1.4", "lru-cache": "^10.0.0", "tslib": "^2.8.1" }, "peerDependencies": { "graphql": "^15.2.0 || ^16.0.0" } }, "sha512-a0DxeXr2oazOlnh8i+By8EM8QJPIG9OcI/nB6K//paM6fjv97WTYgXd57r0Ni0yOm6ts+y1yYL5IG90N4UWFmQ=="], "har-schema": ["har-schema@2.0.0", "", {}, "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q=="], @@ -261,6 +340,10 @@ "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], @@ -271,24 +354,44 @@ "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], "is-typedarray": ["is-typedarray@1.0.0", "", {}, "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "isomorphic-ws": ["isomorphic-ws@5.0.0", "", { "peerDependencies": { "ws": "*" } }, "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw=="], + "isstream": ["isstream@0.1.2", "", {}, "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="], + "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + "jsbn": ["jsbn@0.1.1", "", {}, "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="], + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "jsonfile": ["jsonfile@2.4.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw=="], "jsprim": ["jsprim@1.4.2", "", { "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.4.0", "verror": "1.10.0" } }, "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw=="], @@ -297,8 +400,14 @@ "klaw": ["klaw@1.3.1", "", { "optionalDependencies": { "graceful-fs": "^4.1.9" } }, "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw=="], + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "lodash.sortby": ["lodash.sortby@4.7.0", "", {}, "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="], + + "lower-case": ["lower-case@2.0.2", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg=="], + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], @@ -307,11 +416,17 @@ "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "meros": ["meros@1.3.1", "", { "peerDependencies": { "@types/node": ">=13" }, "optionalPeers": ["@types/node"] }, "sha512-eV7dRObfTrckdmAz4/n7pT1njIsIJXRIZkgCiX43xEsPNy4gjXQzOYYxmGcolAMtF7HyfqRuDBh3Lgs4hmhVEw=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], "mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -321,6 +436,14 @@ "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + "no-case": ["no-case@3.0.4", "", { "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg=="], + + "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], + + "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], + + "normalize-path": ["normalize-path@2.1.1", "", { "dependencies": { "remove-trailing-separator": "^1.0.1" } }, "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w=="], + "number-is-nan": ["number-is-nan@1.0.1", "", {}, "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ=="], "oauth-sign": ["oauth-sign@0.9.0", "", {}, "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="], @@ -337,6 +460,12 @@ "os-homedir": ["os-homedir@1.0.2", "", {}, "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ=="], + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], @@ -345,8 +474,14 @@ "path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="], + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + "performance-now": ["performance-now@2.1.0", "", {}, "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="], + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "pkce-challenge": ["pkce-challenge@5.0.0", "", {}, "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ=="], "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], @@ -357,6 +492,8 @@ "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], "raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="], @@ -365,20 +502,28 @@ "regenerator-runtime": ["regenerator-runtime@0.9.6", "", {}, "sha512-D0Y/JJ4VhusyMOd/o25a3jdUqN/bC85EFsaoL9Oqmy/O4efCh+xhp7yj2EEOsj974qvMkcW8AwUzJ1jB/MbxCw=="], + "remove-trailing-separator": ["remove-trailing-separator@1.1.0", "", {}, "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw=="], + "request": ["request@2.88.2", "", { "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", "caseless": "~0.12.0", "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "oauth-sign": "~0.9.0", "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" } }, "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw=="], "request-promise": ["request-promise@3.0.0", "", { "dependencies": { "bluebird": "^3.3", "lodash": "^4.6.1", "request": "^2.34" } }, "sha512-wVGUX+BoKxYsavTA72i6qHcyLbjzM4LR4y/AmDCqlbuMAursZdDWO7PmgbGAUvD2SeEJ5iB99VSq/U51i/DNbw=="], "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], + "restore-cursor": ["restore-cursor@1.0.1", "", { "dependencies": { "exit-hook": "^1.0.0", "onetime": "^1.0.0" } }, "sha512-reSjH4HuiFlxlaBaFCiS6O76ZGG2ygKoSlCsipKdaZuKSPx/+bt9mULkn4l0asVzbEfQQmXRg6Wp6gv6m0wElw=="], + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + "rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="], "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], "run-async": ["run-async@0.1.0", "", { "dependencies": { "once": "^1.3.0" } }, "sha512-qOX+w+IxFgpUpJfkv2oGN0+ExPs68F4sZHfaRRx4dDexAQkG83atugKVEylyT5ARees3HBbfmuvnjbrd8j9Wjw=="], + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + "rx-lite": ["rx-lite@3.1.2", "", {}, "sha512-1I1+G2gteLB8Tkt8YI1sJvSIfa0lWuRtC8GjvtyPBcLSF5jBCCJJqKrpER5JU5r6Bhe+i9/pK3VMuUcXu0kdwQ=="], "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], @@ -403,22 +548,32 @@ "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + "sshpk": ["sshpk@1.18.0", "", { "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", "dashdash": "^1.12.0", "ecc-jsbn": "~0.1.1", "getpass": "^0.1.1", "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" }, "bin": { "sshpk-conv": "bin/sshpk-conv", "sshpk-sign": "bin/sshpk-sign", "sshpk-verify": "bin/sshpk-verify" } }, "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ=="], "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + "string-env-interpolation": ["string-env-interpolation@1.0.1", "", {}, "sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg=="], + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "strip-ansi": ["strip-ansi@3.0.1", "", { "dependencies": { "ansi-regex": "^2.0.0" } }, "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg=="], "supports-color": ["supports-color@2.0.0", "", {}, "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g=="], + "sync-fetch": ["sync-fetch@0.6.0-2", "", { "dependencies": { "node-fetch": "^3.3.2", "timeout-signal": "^2.0.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-c7AfkZ9udatCuAy9RSfiGPpeOKKUAUK5e1cXadLOGUjasdxqYqAK0jTNkM/FSEyJ3a5Ra27j/tw/PS0qLmaF/A=="], + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], "through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="], + "timeout-signal": ["timeout-signal@2.0.0", "", {}, "sha512-YBGpG4bWsHoPvofT6y/5iqulfXIiIErl5B0LdtHT1mGXDFTAhhRrbUpTvBgYbovr+3cKblya2WAOcpoy90XguA=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], "tough-cookie": ["tough-cookie@2.5.0", "", { "dependencies": { "psl": "^1.1.28", "punycode": "^2.1.1" } }, "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g=="], @@ -435,10 +590,14 @@ "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "unixify": ["unixify@1.0.0", "", { "dependencies": { "normalize-path": "^2.1.1" } }, "sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], "untildify": ["untildify@3.0.3", "", {}, "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA=="], + "upper-case": ["upper-case@2.0.2", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg=="], + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], "urlpattern-polyfill": ["urlpattern-polyfill@10.0.0", "", {}, "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg=="], @@ -451,26 +610,76 @@ "verror": ["verror@1.10.0", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw=="], + "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], + + "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "zod": ["zod@3.25.30", "", {}, "sha512-VolhdEtu6TJr/fzGuHA/SZ5ixvXqA6ADOG9VRcQ3rdOKmF5hkmcJbyaQjUH5BgmpA9gej++zYRX7zjSmdReIwA=="], "zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="], + "@graphql-tools/batch-execute/@graphql-tools/utils": ["@graphql-tools/utils@10.9.1", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw=="], + + "@graphql-tools/delegate/@graphql-tools/executor": ["@graphql-tools/executor@1.4.9", "", { "dependencies": { "@graphql-tools/utils": "^10.9.1", "@graphql-typed-document-node/core": "^3.2.0", "@repeaterjs/repeater": "^3.0.4", "@whatwg-node/disposablestack": "^0.0.6", "@whatwg-node/promise-helpers": "^1.0.0", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-SAUlDT70JAvXeqV87gGzvDzUGofn39nvaVcVhNf12Dt+GfWHtNNO/RCn/Ea4VJaSLGzraUd41ObnN3i80EBU7w=="], + + "@graphql-tools/delegate/@graphql-tools/schema": ["@graphql-tools/schema@10.0.25", "", { "dependencies": { "@graphql-tools/merge": "^9.1.1", "@graphql-tools/utils": "^10.9.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-/PqE8US8kdQ7lB9M5+jlW8AyVjRGCKU7TSktuW3WNKSKmDO0MK1wakvb5gGdyT49MjAIb4a3LWxIpwo5VygZuw=="], + + "@graphql-tools/delegate/@graphql-tools/utils": ["@graphql-tools/utils@10.9.1", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw=="], + + "@graphql-tools/executor-common/@envelop/core": ["@envelop/core@5.3.1", "", { "dependencies": { "@envelop/instrumentation": "^1.0.0", "@envelop/types": "^5.2.1", "@whatwg-node/promise-helpers": "^1.2.4", "tslib": "^2.5.0" } }, "sha512-n29V3vRqXvPcG76C8zE482LQykk0P66zv1mjpk7aHeGe9qnh8AzB/RvoX5SVFwApJQPp0ixob8NoYXg4FHKMGA=="], + + "@graphql-tools/executor-common/@graphql-tools/utils": ["@graphql-tools/utils@10.9.1", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw=="], + + "@graphql-tools/executor-graphql-ws/@graphql-tools/utils": ["@graphql-tools/utils@10.9.1", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw=="], + + "@graphql-tools/executor-http/@graphql-tools/executor-common": ["@graphql-tools/executor-common@0.0.4", "", { "dependencies": { "@envelop/core": "^5.2.3", "@graphql-tools/utils": "^10.8.1" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-SEH/OWR+sHbknqZyROCFHcRrbZeUAyjCsgpVWCRjqjqRbiJiXq6TxNIIOmpXgkrXWW/2Ev4Wms6YSGJXjdCs6Q=="], + + "@graphql-tools/executor-http/@graphql-tools/utils": ["@graphql-tools/utils@10.9.1", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw=="], + + "@graphql-tools/executor-legacy-ws/@graphql-tools/utils": ["@graphql-tools/utils@10.9.1", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw=="], + + "@graphql-tools/graphql-file-loader/@graphql-tools/utils": ["@graphql-tools/utils@10.9.1", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw=="], + + "@graphql-tools/import/@graphql-tools/utils": ["@graphql-tools/utils@10.9.1", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw=="], + + "@graphql-tools/json-file-loader/@graphql-tools/utils": ["@graphql-tools/utils@10.9.1", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw=="], + + "@graphql-tools/load/@graphql-tools/schema": ["@graphql-tools/schema@10.0.25", "", { "dependencies": { "@graphql-tools/merge": "^9.1.1", "@graphql-tools/utils": "^10.9.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-/PqE8US8kdQ7lB9M5+jlW8AyVjRGCKU7TSktuW3WNKSKmDO0MK1wakvb5gGdyT49MjAIb4a3LWxIpwo5VygZuw=="], + + "@graphql-tools/load/@graphql-tools/utils": ["@graphql-tools/utils@10.9.1", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw=="], + + "@graphql-tools/url-loader/@graphql-tools/utils": ["@graphql-tools/utils@10.9.1", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw=="], + + "@graphql-tools/wrap/@graphql-tools/schema": ["@graphql-tools/schema@10.0.25", "", { "dependencies": { "@graphql-tools/merge": "^9.1.1", "@graphql-tools/utils": "^10.9.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-/PqE8US8kdQ7lB9M5+jlW8AyVjRGCKU7TSktuW3WNKSKmDO0MK1wakvb5gGdyT49MjAIb4a3LWxIpwo5VygZuw=="], + + "@graphql-tools/wrap/@graphql-tools/utils": ["@graphql-tools/utils@10.9.1", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@whatwg-node/promise-helpers": "^1.0.0", "cross-inspect": "1.0.1", "dset": "^3.1.4", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw=="], + + "@theguild/federation-composition/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + "inquirer/lodash": ["lodash@3.10.1", "", {}, "sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ=="], "inquirer/string-width": ["string-width@1.0.2", "", { "dependencies": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", "strip-ansi": "^3.0.0" } }, "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw=="], @@ -487,10 +696,20 @@ "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@graphql-tools/delegate/@graphql-tools/schema/@graphql-tools/merge": ["@graphql-tools/merge@9.1.1", "", { "dependencies": { "@graphql-tools/utils": "^10.9.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-BJ5/7Y7GOhTuvzzO5tSBFL4NGr7PVqTJY3KeIDlVTT8YLcTXtBR+hlrC3uyEym7Ragn+zyWdHeJ9ev+nRX1X2w=="], + + "@graphql-tools/executor-http/@graphql-tools/executor-common/@envelop/core": ["@envelop/core@5.3.1", "", { "dependencies": { "@envelop/instrumentation": "^1.0.0", "@envelop/types": "^5.2.1", "@whatwg-node/promise-helpers": "^1.2.4", "tslib": "^2.5.0" } }, "sha512-n29V3vRqXvPcG76C8zE482LQykk0P66zv1mjpk7aHeGe9qnh8AzB/RvoX5SVFwApJQPp0ixob8NoYXg4FHKMGA=="], + + "@graphql-tools/load/@graphql-tools/schema/@graphql-tools/merge": ["@graphql-tools/merge@9.1.1", "", { "dependencies": { "@graphql-tools/utils": "^10.9.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-BJ5/7Y7GOhTuvzzO5tSBFL4NGr7PVqTJY3KeIDlVTT8YLcTXtBR+hlrC3uyEym7Ragn+zyWdHeJ9ev+nRX1X2w=="], + + "@graphql-tools/wrap/@graphql-tools/schema/@graphql-tools/merge": ["@graphql-tools/merge@9.1.1", "", { "dependencies": { "@graphql-tools/utils": "^10.9.1", "tslib": "^2.4.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-BJ5/7Y7GOhTuvzzO5tSBFL4NGr7PVqTJY3KeIDlVTT8YLcTXtBR+hlrC3uyEym7Ragn+zyWdHeJ9ev+nRX1X2w=="], + "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + "inquirer/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@1.0.0", "", { "dependencies": { "number-is-nan": "^1.0.0" } }, "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw=="], "request/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], diff --git a/graphql.config.example.js b/graphql.config.example.js new file mode 100644 index 0000000..d0ee574 --- /dev/null +++ b/graphql.config.example.js @@ -0,0 +1,79 @@ +// GraphQL Config in JavaScript format +// Rename to graphql.config.js to use + +module.exports = { + // Schema can be a function for dynamic resolution + schema: async () => { + const endpoint = process.env.SCHEMA || 'http://localhost:4000/graphql'; + return endpoint; + }, + + // Documents - GraphQL operations to load + documents: [ + './graphql/**/*.graphql', + './graphql/**/*.gql', + './operations/**/*.graphql' + ], + + // Extensions for additional configuration + extensions: { + // Endpoint configuration + endpoints: { + default: { + url: process.env.ENDPOINT || 'http://localhost:4000/graphql', + headers: () => ({ + 'Authorization': `Bearer ${process.env.API_TOKEN || ''}`, + 'Content-Type': 'application/json', + // Add custom headers dynamically + ...(process.env.CUSTOM_HEADERS ? JSON.parse(process.env.CUSTOM_HEADERS) : {}) + }) + } + }, + + // Custom configuration for MCP + mcp: { + // Allow mutations (default: false) + allowMutations: process.env.ALLOW_MUTATIONS === 'true', + // Tool name prefix + toolPrefix: 'gql', + // Max operations to load (prevent overload) + maxOperations: 100 + } + }, + + // Multi-project configuration example + // Uncomment to use multiple projects + /* + projects: { + production: { + schema: 'https://api.example.com/graphql', + documents: './graphql/production/*.graphql', + extensions: { + endpoints: { + default: { + url: 'https://api.example.com/graphql', + headers: { + 'Authorization': `Bearer ${process.env.PROD_TOKEN}` + } + } + } + } + }, + + development: { + schema: 'http://localhost:4000/graphql', + documents: './graphql/dev/*.graphql', + extensions: { + endpoints: { + default: { + url: 'http://localhost:4000/graphql', + headers: { + 'Authorization': `Bearer ${process.env.DEV_TOKEN}` + } + } + } + } + } + } + */ +}; \ No newline at end of file diff --git a/package.json b/package.json index c120d1a..a8c582a 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "1.12.0", "graphql": "^16.11.0", + "graphql-config": "^5.1.5", "yargs": "17.7.2", "zod": "3.25.30", "zod-to-json-schema": "3.24.5" diff --git a/src/helpers/graphql-config.ts b/src/helpers/graphql-config.ts new file mode 100644 index 0000000..99143a6 --- /dev/null +++ b/src/helpers/graphql-config.ts @@ -0,0 +1,247 @@ +import { loadConfig, type GraphQLConfig } from "graphql-config"; +import { parse, type OperationDefinitionNode, type VariableDefinitionNode } from "graphql"; +import { z } from "zod"; +import { readFile } from "node:fs/promises"; +import { existsSync } from "node:fs"; + +export interface GraphQLConfigOperation { + name: string; + type: "query" | "mutation" | "subscription"; + content: string; + variables: VariableDefinitionNode[]; + filePath: string; + project?: string; +} + +export interface ConfigWithOperations { + config: GraphQLConfig; + operations: GraphQLConfigOperation[]; + endpoint?: string; + headers?: Record; +} + +/** + * Load GraphQL Config and fallback to environment variables if not found + */ +export async function loadGraphQLConfig( + rootDir: string = process.cwd(), + env: { + ENDPOINT?: string; + HEADERS?: Record; + SCHEMA?: string; + GRAPHQL_DIR?: string; + }, +): Promise { + try { + // Try to load GraphQL Config file + const config = await loadConfig({ + rootDir, + throwOnMissing: false, + throwOnEmpty: false, + }); + + if (!config || !config.filepath) { + // No config file found, use environment variables + return null; + } + + // Get endpoint and headers from extensions if available + const extensions = config.rawConfig?.extensions as any; + const endpoint = extensions?.endpoints?.default?.url || env.ENDPOINT; + const headers = extensions?.endpoints?.default?.headers || env.HEADERS; + + // Load all operations from documents + const operations = await loadOperationsFromConfig(config); + + return { + config, + operations, + endpoint, + headers: typeof headers === 'string' ? JSON.parse(headers) : headers, + }; + } catch (error) { + console.error("Failed to load GraphQL Config:", error); + return null; + } +} + +/** + * Load all GraphQL operations from config documents + */ +async function loadOperationsFromConfig( + config: GraphQLConfig, +): Promise { + const operations: GraphQLConfigOperation[] = []; + + // Handle single project or multiple projects + const projects = config.projects + ? Object.entries(config.projects) + : [['default', config]]; + + for (const [projectName, projectConfig] of projects) { + try { + // Get documents for this project + const documents = await projectConfig.getDocuments(); + + if (!documents || documents.length === 0) { + continue; + } + + // Parse operations from each document + for (const doc of documents) { + if (!doc.rawSDL || !doc.location) { + continue; + } + + try { + const parsed = parse(doc.rawSDL); + + for (const definition of parsed.definitions) { + if (definition.kind === "OperationDefinition") { + const operationDef = definition as OperationDefinitionNode; + + // Use operation name or derive from file + const operationName = operationDef.name?.value || + getOperationNameFromPath(doc.location); + + operations.push({ + name: operationName, + type: operationDef.operation, + content: doc.rawSDL, + variables: operationDef.variableDefinitions || [], + filePath: doc.location, + project: projects.length > 1 ? projectName : undefined, + }); + } + } + } catch (error) { + console.error(`Failed to parse document ${doc.location}:`, error); + } + } + } catch (error) { + console.error(`Failed to load documents for project ${projectName}:`, error); + } + } + + return operations; +} + +/** + * Fallback to load operations from directory (for backward compatibility) + */ +export async function loadOperationsFromDirectory( + directory: string, +): Promise { + if (!existsSync(directory)) { + return []; + } + + const { loadAllGraphQLOperations } = await import("./graphql-files.js"); + const operations = await loadAllGraphQLOperations(directory); + + return operations.map(op => ({ + ...op, + project: undefined, + })); +} + +/** + * Extract operation name from file path + */ +function getOperationNameFromPath(filePath: string): string { + const parts = filePath.split('/'); + const filename = parts[parts.length - 1]; + return filename.replace(/\.(graphql|gql)$/, ''); +} + +/** + * Convert GraphQL variable types to Zod schema + */ +function getGraphQLTypeAsZod(type: any): z.ZodTypeAny { + // Handle NonNull types + if (type.kind === "NonNullType") { + return getGraphQLTypeAsZod(type.type); + } + + // Handle List types + if (type.kind === "ListType") { + return z.array(getGraphQLTypeAsZod(type.type)).optional(); + } + + // Handle Named types + if (type.kind === "NamedType") { + switch (type.name.value) { + case "String": + return z.string(); + case "Int": + return z.number().int(); + case "Float": + return z.number(); + case "Boolean": + return z.boolean(); + case "ID": + return z.string(); + default: + // For custom types, treat as JSON string + return z.string().describe(`GraphQL ${type.name.value} type`); + } + } + + // Default to string for unknown types + return z.string(); +} + +/** + * Create Zod schema from GraphQL variables + */ +export function createVariableSchema( + variables: VariableDefinitionNode[], +): z.ZodObject { + const schemaShape: Record = {}; + + for (const variable of variables) { + const varName = variable.variable.name.value; + let zodType = getGraphQLTypeAsZod(variable.type); + + // Add default value if present + if (variable.defaultValue) { + zodType = zodType.optional(); + } + + // Check if the variable type is NonNull (required) + if (variable.type.kind !== "NonNullType") { + zodType = zodType.optional(); + } + + schemaShape[varName] = zodType; + } + + return z.object(schemaShape); +} + +/** + * Generate tool name for MCP + */ +export function generateToolName(operation: GraphQLConfigOperation): string { + // Convert operation name to kebab-case + const kebabName = operation.name + .replace(/([A-Z])/g, "-$1") + .toLowerCase() + .replace(/^-/, ""); + + // Include project name if multiple projects + const prefix = operation.project && operation.project !== 'default' + ? `gql-${operation.project}-` + : "gql-"; + + return `${prefix}${kebabName}`; +} + +/** + * Generate tool description for MCP + */ +export function generateToolDescription(operation: GraphQLConfigOperation): string { + const operationType = operation.type.charAt(0).toUpperCase() + operation.type.slice(1); + const project = operation.project ? ` [${operation.project}]` : ""; + return `${operationType} GraphQL operation: ${operation.name}${project}`; +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index ef09c61..54b66e9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,12 +12,13 @@ import { } from "./helpers/introspection.js"; import { getVersion } from "./helpers/package.js" with { type: "macro" }; import { - loadAllGraphQLOperations, + loadGraphQLConfig, + loadOperationsFromDirectory, createVariableSchema, generateToolName, generateToolDescription, - type GraphQLOperation, -} from "./helpers/graphql-files.js"; + type GraphQLConfigOperation, +} from "./helpers/graphql-config.js"; // Check for deprecated command line arguments checkDeprecatedArguments(); @@ -223,12 +224,29 @@ server.tool( ); async function registerGraphQLFileTools() { - const operations = await loadAllGraphQLOperations(env.GRAPHQL_DIR); + // Try to load GraphQL Config first + const configResult = await loadGraphQLConfig(process.cwd(), env); - if (operations.length > 0) { + let operations: GraphQLConfigOperation[]; + let endpoint = env.ENDPOINT; + let headers = env.HEADERS; + + if (configResult) { + // Use GraphQL Config + operations = configResult.operations; + endpoint = configResult.endpoint || env.ENDPOINT; + headers = configResult.headers || env.HEADERS; console.error( - `Found ${operations.length} GraphQL operations in ${env.GRAPHQL_DIR}`, + `Loaded ${operations.length} operations from GraphQL Config`, ); + } else { + // Fallback to directory scanning + operations = await loadOperationsFromDirectory(env.GRAPHQL_DIR); + if (operations.length > 0) { + console.error( + `Found ${operations.length} GraphQL operations in ${env.GRAPHQL_DIR}`, + ); + } } for (const operation of operations) { @@ -250,11 +268,11 @@ async function registerGraphQLFileTools() { variableSchema.shape, async (variables) => { try { - const response = await fetch(env.ENDPOINT, { + const response = await fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json", - ...env.HEADERS, + ...headers, }, body: JSON.stringify({ query: operation.content, From 42819515b0b19a941fbb1e8c69b0829c19a0f951 Mon Sep 17 00:00:00 2001 From: Boris Besemer Date: Mon, 8 Sep 2025 15:53:05 +0200 Subject: [PATCH 4/5] refactor: organize examples into dedicated folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructure example files for better organization and package distribution: - Move all examples to examples/ directory - Add .npmignore to exclude examples from npm package - Create comprehensive examples/README.md with usage instructions - Organize configs (YAML, JSON, JS) in examples/config/ - Keep GraphQL operation examples in examples/graphql/ This ensures examples are available in the repository but not distributed in the npm package, reducing package size and keeping production clean. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .graphqlrc.yml | 8 +- .npmignore | 41 +++++ README.md | 62 +++---- examples/README.md | 174 ++++++++++++++++++ .../config/graphql.config.js | 9 +- .../config/graphqlrc.json | 4 +- examples/config/graphqlrc.yml | 51 +++++ .../graphql}/mutations/createPost.graphql | 0 .../graphql}/mutations/updateUser.graphql | 0 .../graphql}/queries/getUser.graphql | 0 .../graphql}/queries/listPosts.graphql | 0 .../graphql}/queries/searchUsers.graphql | 0 12 files changed, 297 insertions(+), 52 deletions(-) create mode 100644 .npmignore create mode 100644 examples/README.md rename graphql.config.example.js => examples/config/graphql.config.js (91%) rename .graphqlrc.example.json => examples/config/graphqlrc.json (81%) create mode 100644 examples/config/graphqlrc.yml rename {graphql => examples/graphql}/mutations/createPost.graphql (100%) rename {graphql => examples/graphql}/mutations/updateUser.graphql (100%) rename {graphql => examples/graphql}/queries/getUser.graphql (100%) rename {graphql => examples/graphql}/queries/listPosts.graphql (100%) rename {graphql => examples/graphql}/queries/searchUsers.graphql (100%) diff --git a/.graphqlrc.yml b/.graphqlrc.yml index 07ca027..e74302c 100644 --- a/.graphqlrc.yml +++ b/.graphqlrc.yml @@ -9,10 +9,10 @@ schema: ${SCHEMA:-http://localhost:4000/graphql} # Documents - GraphQL operations to load as MCP tools # Supports glob patterns +# Update these paths to match your project structure documents: - - './graphql/**/*.graphql' - - './graphql/**/*.gql' - './operations/**/*.graphql' + - './operations/**/*.gql' # Extensions for additional configuration extensions: @@ -32,7 +32,7 @@ extensions: # # Production API # production: # schema: https://api.example.com/graphql -# documents: './graphql/production/**/*.graphql' +# documents: './operations/production/**/*.graphql' # extensions: # endpoints: # default: @@ -43,7 +43,7 @@ extensions: # # Development API # development: # schema: http://localhost:4000/graphql -# documents: './graphql/dev/**/*.graphql' +# documents: './operations/dev/**/*.graphql' # extensions: # endpoints: # default: diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..4d486cc --- /dev/null +++ b/.npmignore @@ -0,0 +1,41 @@ +# Development files +dev/ +examples/ +src/ +test/ +*.ts +!dist/**/*.d.ts + +# Configuration files +.graphqlrc.yml +.graphqlrc.json +.graphqlrc +graphql.config.js +tsconfig.json +biome.json +bun.lock + +# Documentation +CLAUDE.md +*.example.* + +# Git and CI +.git/ +.github/ +.gitignore + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* \ No newline at end of file diff --git a/README.md b/README.md index a61ba9e..fe6cc9b 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ Create a `.graphqlrc.yml` file in your project root: ```yaml schema: http://localhost:4000/graphql documents: - - './graphql/**/*.graphql' - - './operations/**/*.gql' + - './operations/**/*.graphql' + - './queries/**/*.gql' extensions: endpoints: default: @@ -34,6 +34,8 @@ extensions: Authorization: Bearer ${API_TOKEN} ``` +**See [`examples/`](./examples) directory for complete configuration examples.** + Supported config formats: - `.graphqlrc.yml` / `.graphqlrc.yaml` - `.graphqlrc.json` @@ -151,48 +153,25 @@ The server now supports [GraphQL Config](https://graphql-config.com/), the stand - Organize operations by type or domain - Type-check operations against your schema -### Multi-Project Configuration - -Support multiple GraphQL APIs in a single configuration: - -```yaml -# .graphqlrc.yml -projects: - production: - schema: https://api.example.com/graphql - documents: './graphql/production/**/*.graphql' - extensions: - endpoints: - default: - url: https://api.example.com/graphql - headers: - Authorization: Bearer ${PROD_TOKEN} - - development: - schema: http://localhost:4000/graphql - documents: './graphql/dev/**/*.graphql' - extensions: - endpoints: - default: - url: http://localhost:4000/graphql -``` +### Examples -Tools from multi-project configs are named: `gql-{project}-{operation}` +The [`examples/`](./examples) directory contains: +- **Configuration examples** in YAML, JSON, and JavaScript formats +- **GraphQL operation examples** for queries and mutations +- **Multi-project setup** for managing multiple APIs -### File Structure Example +Quick start: +```bash +# Copy an example config to your project +cp examples/config/graphqlrc.yml .graphqlrc.yml -``` -graphql/ -├── queries/ -│ ├── getUser.graphql -│ ├── listPosts.graphql -│ └── searchUsers.graphql -└── mutations/ - ├── createPost.graphql - └── updateUser.graphql +# View example operations +ls examples/graphql/ ``` -### Operation File Example +### Operation Files + +Define your GraphQL operations in `.graphql` or `.gql` files: ```graphql # getUser.graphql @@ -201,12 +180,13 @@ query GetUser($id: ID!) { id name email - createdAt } } ``` -This operation will be available as the `gql-get-user` tool, accepting an `id` parameter. +This operation becomes available as the `gql-get-user` MCP tool. + +**See [`examples/graphql/`](./examples/graphql) for more operation examples.** ### Naming Conventions diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..65e31da --- /dev/null +++ b/examples/README.md @@ -0,0 +1,174 @@ +# MCP GraphQL Examples + +This directory contains examples for configuring and using the MCP GraphQL server. + +## Directory Structure + +``` +examples/ +├── config/ # Configuration file examples +│ ├── graphqlrc.yml # YAML format (recommended) +│ ├── graphqlrc.json # JSON format +│ └── graphql.config.js # JavaScript format (dynamic config) +└── graphql/ # Example GraphQL operations + ├── queries/ # Query operations + └── mutations/ # Mutation operations +``` + +## Configuration Examples + +### Basic Configuration (graphqlrc.yml) + +The simplest configuration using YAML format: + +```yaml +schema: http://localhost:4000/graphql +documents: + - './examples/graphql/**/*.graphql' +``` + +### JSON Configuration (graphqlrc.json) + +Same configuration in JSON format: + +```json +{ + "schema": "http://localhost:4000/graphql", + "documents": ["./examples/graphql/**/*.graphql"] +} +``` + +### JavaScript Configuration (graphql.config.js) + +Dynamic configuration with environment variables: + +```javascript +module.exports = { + schema: process.env.GRAPHQL_ENDPOINT || 'http://localhost:4000/graphql', + documents: ['./examples/graphql/**/*.graphql'], + extensions: { + endpoints: { + default: { + url: process.env.GRAPHQL_ENDPOINT, + headers: { + 'Authorization': `Bearer ${process.env.API_TOKEN}` + } + } + } + } +}; +``` + +## GraphQL Operation Examples + +### Query Examples + +**getUser.graphql** - Fetch a user by ID: +```graphql +query GetUser($id: ID!) { + user(id: $id) { + id + name + email + createdAt + } +} +``` + +**listPosts.graphql** - List posts with pagination: +```graphql +query ListPosts($limit: Int = 10, $offset: Int = 0) { + posts(limit: $limit, offset: $offset) { + id + title + content + author { + id + name + } + } +} +``` + +### Mutation Examples + +**createPost.graphql** - Create a new post: +```graphql +mutation CreatePost($title: String!, $content: String!, $authorId: ID!) { + createPost(input: { + title: $title, + content: $content, + authorId: $authorId + }) { + id + title + content + } +} +``` + +## Using the Examples + +1. **Copy a config file to your project root:** + ```bash + cp examples/config/graphqlrc.yml .graphqlrc.yml + ``` + +2. **Adjust the configuration:** + - Update the `schema` to point to your GraphQL endpoint + - Modify `documents` paths to match your file structure + - Add authentication headers if needed + +3. **Create your own operations:** + - Place `.graphql` files in your project + - Reference them in the `documents` field + - They'll automatically become available as MCP tools + +## Multi-Project Setup + +For multiple GraphQL APIs, use the projects configuration: + +```yaml +projects: + production: + schema: https://api.example.com/graphql + documents: './operations/production/**/*.graphql' + extensions: + endpoints: + default: + url: https://api.example.com/graphql + headers: + Authorization: Bearer ${PROD_TOKEN} + + development: + schema: http://localhost:4000/graphql + documents: './operations/dev/**/*.graphql' +``` + +Tools will be named: `gql-production-{operation}` and `gql-development-{operation}` + +## Environment Variables + +GraphQL Config supports environment variable interpolation using `${VAR_NAME}`: + +```yaml +schema: ${GRAPHQL_SCHEMA} +extensions: + endpoints: + default: + url: ${GRAPHQL_ENDPOINT} + headers: + Authorization: Bearer ${API_TOKEN} +``` + +## Tool Naming + +Operations are exposed as MCP tools with the following naming convention: +- Single project: `gql-{operation-name}` +- Multi-project: `gql-{project}-{operation-name}` +- Names are converted to kebab-case + +Examples: +- `GetUser` → `gql-get-user` +- `CreatePost` → `gql-create-post` +- Production `GetUser` → `gql-production-get-user` \ No newline at end of file diff --git a/graphql.config.example.js b/examples/config/graphql.config.js similarity index 91% rename from graphql.config.example.js rename to examples/config/graphql.config.js index d0ee574..a062bf5 100644 --- a/graphql.config.example.js +++ b/examples/config/graphql.config.js @@ -10,9 +10,8 @@ module.exports = { // Documents - GraphQL operations to load documents: [ - './graphql/**/*.graphql', - './graphql/**/*.gql', - './operations/**/*.graphql' + './examples/graphql/**/*.graphql', + './examples/graphql/**/*.gql' ], // Extensions for additional configuration @@ -47,7 +46,7 @@ module.exports = { projects: { production: { schema: 'https://api.example.com/graphql', - documents: './graphql/production/*.graphql', + documents: './examples/graphql/production/*.graphql', extensions: { endpoints: { default: { @@ -62,7 +61,7 @@ module.exports = { development: { schema: 'http://localhost:4000/graphql', - documents: './graphql/dev/*.graphql', + documents: './examples/graphql/dev/*.graphql', extensions: { endpoints: { default: { diff --git a/.graphqlrc.example.json b/examples/config/graphqlrc.json similarity index 81% rename from .graphqlrc.example.json rename to examples/config/graphqlrc.json index 8322ec1..04e6c85 100644 --- a/.graphqlrc.example.json +++ b/examples/config/graphqlrc.json @@ -1,8 +1,8 @@ { "schema": "http://localhost:4000/graphql", "documents": [ - "./graphql/**/*.graphql", - "./graphql/**/*.gql" + "./examples/graphql/**/*.graphql", + "./examples/graphql/**/*.gql" ], "extensions": { "endpoints": { diff --git a/examples/config/graphqlrc.yml b/examples/config/graphqlrc.yml new file mode 100644 index 0000000..4c8f5b1 --- /dev/null +++ b/examples/config/graphqlrc.yml @@ -0,0 +1,51 @@ +# GraphQL Config for mcp-graphql +# This file configures GraphQL operations and schema for the MCP server + +# Schema location - can be: +# - Local file: ./schema.graphql +# - URL: https://api.example.com/graphql +# - Introspection endpoint: http://localhost:4000/graphql +schema: ${SCHEMA:-http://localhost:4000/graphql} + +# Documents - GraphQL operations to load as MCP tools +# Supports glob patterns +documents: + - './examples/graphql/**/*.graphql' + - './examples/graphql/**/*.gql' + +# Extensions for additional configuration +extensions: + # Endpoint configuration + endpoints: + default: + # GraphQL endpoint URL + url: ${ENDPOINT:-http://localhost:4000/graphql} + # Headers for requests (supports environment variables) + headers: + Authorization: Bearer ${API_TOKEN} + Content-Type: application/json + +# Multi-project example (commented out) +# Uncomment to use multiple projects with different configurations +# projects: +# # Production API +# production: +# schema: https://api.example.com/graphql +# documents: './examples/graphql/production/**/*.graphql' +# extensions: +# endpoints: +# default: +# url: https://api.example.com/graphql +# headers: +# Authorization: Bearer ${PROD_TOKEN} +# +# # Development API +# development: +# schema: http://localhost:4000/graphql +# documents: './examples/graphql/dev/**/*.graphql' +# extensions: +# endpoints: +# default: +# url: http://localhost:4000/graphql +# headers: +# Authorization: Bearer ${DEV_TOKEN} \ No newline at end of file diff --git a/graphql/mutations/createPost.graphql b/examples/graphql/mutations/createPost.graphql similarity index 100% rename from graphql/mutations/createPost.graphql rename to examples/graphql/mutations/createPost.graphql diff --git a/graphql/mutations/updateUser.graphql b/examples/graphql/mutations/updateUser.graphql similarity index 100% rename from graphql/mutations/updateUser.graphql rename to examples/graphql/mutations/updateUser.graphql diff --git a/graphql/queries/getUser.graphql b/examples/graphql/queries/getUser.graphql similarity index 100% rename from graphql/queries/getUser.graphql rename to examples/graphql/queries/getUser.graphql diff --git a/graphql/queries/listPosts.graphql b/examples/graphql/queries/listPosts.graphql similarity index 100% rename from graphql/queries/listPosts.graphql rename to examples/graphql/queries/listPosts.graphql diff --git a/graphql/queries/searchUsers.graphql b/examples/graphql/queries/searchUsers.graphql similarity index 100% rename from graphql/queries/searchUsers.graphql rename to examples/graphql/queries/searchUsers.graphql From bc7d9a3b8122d7996fb55ed118e1ec6e03b1ceb3 Mon Sep 17 00:00:00 2001 From: Boris Besemer Date: Mon, 8 Sep 2025 16:26:47 +0200 Subject: [PATCH 5/5] feat: implement proper configuration precedence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Environment variables now always take precedence over GraphQL Config files: - Precedence order: Env vars > GraphQL Config > Defaults - Allows temporary overrides without modifying config files - Useful for debugging, testing, and CI/CD pipelines - Follows 12-factor app principles This is cleaner than having two sets of environment variables and matches developer expectations that env vars override file-based configuration. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- CLAUDE.md | 7 ++-- README.md | 59 +++++++++++++++++++++------- src/helpers/graphql-config.ts | 74 +++++++++++++++++++++++------------ src/index.ts | 33 +++++++--------- 4 files changed, 112 insertions(+), 61 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2d334bc..0964f68 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,11 +41,12 @@ When testing GraphQL file loading functionality: ### Key Design Patterns -**Environment Configuration** -- All configuration via environment variables (no CLI args as of v1.0.0) +**Configuration Precedence** +- Environment variables ALWAYS override config files when set +- Order: Env vars > GraphQL Config > Defaults - `ALLOW_MUTATIONS` defaults to `false` for safety -- `GRAPHQL_DIR` enables file-based operation loading - Headers passed as JSON string in `HEADERS` env var +- Supports standard GraphQL Config formats (.graphqlrc.yml, .json, .js) **Tool Registration Flow** 1. On startup, scan `GRAPHQL_DIR` for GraphQL files diff --git a/README.md b/README.md index fe6cc9b..2a6a00e 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,27 @@ Run `mcp-graphql` with either a GraphQL Config file or environment variables. Th ### Configuration Methods -The server supports two configuration methods (in priority order): +The server supports two configuration methods with clear precedence: -1. **GraphQL Config File** (Recommended) - Standard `.graphqlrc.yml`, `.graphqlrc.json`, or `graphql.config.js` -2. **Environment Variables** - For backward compatibility and simple setups +1. **Environment Variables** (highest priority) - Always override other settings +2. **GraphQL Config File** - Standard `.graphqlrc.yml`, `.graphqlrc.json`, or `graphql.config.js` +3. **Default Values** - Built-in fallbacks + +#### Precedence Order + +Environment variables **always** take precedence over config files. This allows: +- Temporary overrides without changing files +- Different settings per deployment +- Easy debugging and testing +- CI/CD pipeline customization + +Example: +```bash +# Override config file temporarily +ENDPOINT=http://staging.api.com/graphql npm start + +# The ENDPOINT env var overrides any value in .graphqlrc.yml +``` ### GraphQL Config (Recommended) @@ -45,7 +62,7 @@ Supported config formats: ### Environment Variables -> **Note:** Environment variables are used as fallbacks when GraphQL Config is not present. +> **Note:** Environment variables always take precedence over GraphQL Config values when set. | Environment Variable | Description | Default | |----------|-------------|---------| @@ -96,18 +113,32 @@ This uses either the local schema file, a schema file hosted at a URL, or an int 3. **Dynamic tools from .graphql files**: Any GraphQL operations defined in `.graphql` or `.gql` files within the `GRAPHQL_DIR` directory are automatically registered as MCP tools. Tool names follow the pattern `gql-{operation-name}` (e.g., `gql-get-user`, `gql-create-post`). -## Migration from Environment Variables +## Configuration Precedence + +The server uses a clear precedence order for configuration: + +``` +1. Environment Variables (highest priority) +2. GraphQL Config File +3. Default Values (lowest priority) +``` + +This means: +- Setting `ENDPOINT=http://localhost:5000` will **always** override the config file +- Useful for temporary changes without modifying files +- Follows 12-factor app principles + +### Migration Guide -To migrate from environment variables to GraphQL Config: +To use GraphQL Config while maintaining flexibility: -1. Create a `.graphqlrc.yml` file -2. Map your environment variables: - - `ENDPOINT` → `extensions.endpoints.default.url` - - `HEADERS` → `extensions.endpoints.default.headers` - - `SCHEMA` → `schema` - - `GRAPHQL_DIR` → `documents` (as glob patterns) -3. Remove `GRAPHQL_DIR` from your environment -4. Keep other env vars as fallbacks or use `${ENV_VAR}` in config +1. Create a `.graphqlrc.yml` file with your base configuration +2. Use environment variables for deployment-specific overrides +3. Config file paths: + - `ENDPOINT` overrides `extensions.endpoints.default.url` + - `HEADERS` overrides `extensions.endpoints.default.headers` + - `SCHEMA` overrides `schema` + - `GRAPHQL_DIR` provides fallback if no `documents` in config ## Installation diff --git a/src/helpers/graphql-config.ts b/src/helpers/graphql-config.ts index 99143a6..b4b4571 100644 --- a/src/helpers/graphql-config.ts +++ b/src/helpers/graphql-config.ts @@ -14,14 +14,17 @@ export interface GraphQLConfigOperation { } export interface ConfigWithOperations { - config: GraphQLConfig; + config: GraphQLConfig | null; operations: GraphQLConfigOperation[]; - endpoint?: string; - headers?: Record; + endpoint: string; + headers: Record; } /** - * Load GraphQL Config and fallback to environment variables if not found + * Load GraphQL Config and resolve configuration with proper precedence: + * 1. Environment variables (highest priority) + * 2. GraphQL Config file + * 3. Default values */ export async function loadGraphQLConfig( rootDir: string = process.cwd(), @@ -30,39 +33,60 @@ export async function loadGraphQLConfig( HEADERS?: Record; SCHEMA?: string; GRAPHQL_DIR?: string; + ALLOW_MUTATIONS?: boolean; }, -): Promise { +): Promise { + let config: GraphQLConfig | null = null; + let operations: GraphQLConfigOperation[] = []; + try { // Try to load GraphQL Config file - const config = await loadConfig({ + config = await loadConfig({ rootDir, throwOnMissing: false, throwOnEmpty: false, }); - if (!config || !config.filepath) { - // No config file found, use environment variables - return null; + if (config && config.filepath) { + // Load operations from config documents + operations = await loadOperationsFromConfig(config); } - - // Get endpoint and headers from extensions if available - const extensions = config.rawConfig?.extensions as any; - const endpoint = extensions?.endpoints?.default?.url || env.ENDPOINT; - const headers = extensions?.endpoints?.default?.headers || env.HEADERS; - - // Load all operations from documents - const operations = await loadOperationsFromConfig(config); - - return { - config, - operations, - endpoint, - headers: typeof headers === 'string' ? JSON.parse(headers) : headers, - }; } catch (error) { console.error("Failed to load GraphQL Config:", error); - return null; } + + // If no config or no operations from config, try directory fallback + if (operations.length === 0 && env.GRAPHQL_DIR) { + operations = await loadOperationsFromDirectory(env.GRAPHQL_DIR); + } + + // Resolve configuration with proper precedence: + // 1. Environment variables (highest priority) + // 2. GraphQL Config file + // 3. Default values + const extensions = config?.rawConfig?.extensions as any; + + const endpoint = + env.ENDPOINT || // Environment variable takes precedence + extensions?.endpoints?.default?.url || // Then config file + 'http://localhost:4000/graphql'; // Default + + // Parse headers with precedence + let headers: Record = {}; + if (env.HEADERS) { + // Environment variable takes precedence + headers = typeof env.HEADERS === 'string' ? JSON.parse(env.HEADERS) : env.HEADERS; + } else if (extensions?.endpoints?.default?.headers) { + // Then config file + headers = extensions.endpoints.default.headers; + } + + return { + config, + operations, + endpoint, + headers, + }; } /** diff --git a/src/index.ts b/src/index.ts index 54b66e9..9983c6a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,7 +13,6 @@ import { import { getVersion } from "./helpers/package.js" with { type: "macro" }; import { loadGraphQLConfig, - loadOperationsFromDirectory, createVariableSchema, generateToolName, generateToolDescription, @@ -224,29 +223,25 @@ server.tool( ); async function registerGraphQLFileTools() { - // Try to load GraphQL Config first + // Load configuration with proper precedence: + // 1. Environment variables (highest priority) + // 2. GraphQL Config file + // 3. Default values const configResult = await loadGraphQLConfig(process.cwd(), env); - let operations: GraphQLConfigOperation[]; - let endpoint = env.ENDPOINT; - let headers = env.HEADERS; + const { operations, endpoint, headers, config } = configResult; - if (configResult) { - // Use GraphQL Config - operations = configResult.operations; - endpoint = configResult.endpoint || env.ENDPOINT; - headers = configResult.headers || env.HEADERS; + if (config) { console.error( - `Loaded ${operations.length} operations from GraphQL Config`, + `Loaded GraphQL Config from ${config.filepath}`, + ); + } + + if (operations.length > 0) { + const source = config ? 'GraphQL Config' : env.GRAPHQL_DIR; + console.error( + `Found ${operations.length} operations from ${source}`, ); - } else { - // Fallback to directory scanning - operations = await loadOperationsFromDirectory(env.GRAPHQL_DIR); - if (operations.length > 0) { - console.error( - `Found ${operations.length} GraphQL operations in ${env.GRAPHQL_DIR}`, - ); - } } for (const operation of operations) {