@turbot/guardrails-mcp
Version:
MCP server for interacting with Turbot Guardrails.
168 lines (163 loc) • 5.67 kB
JavaScript
import { executeQuery } from "../utils/graphqlClient.js";
import { logger } from '../services/pinoLogger.js';
import { formatJsonToolResponse, errorResponse } from '../utils/responseFormatter.mjs';
// Query to get the root query type and available types
const ROOT_SCHEMA_QUERY = `
query IntrospectionQuery {
__schema {
queryType {
name
fields {
name
description
type {
name
kind
}
}
}
types {
name
kind
description
}
}
}
`;
// Query to get detailed information about a specific type
const TYPE_DETAILS_QUERY = `
query TypeQuery($typeName: String!) {
__type(name: $typeName) {
name
kind
description
fields {
name
description
type {
name
kind
ofType {
name
kind
}
}
args {
name
description
type {
name
kind
ofType {
name
kind
}
}
defaultValue
}
}
inputFields {
name
description
type {
name
kind
ofType {
name
kind
}
}
defaultValue
}
enumValues {
name
description
}
}
}
`;
export const tool = {
name: "guardrails_query_runnable_introspection",
description: `
Introspect the GraphQL schema for a specific Guardrails runnable type (such as a policy or control type).
This tool helps you discover the structure, fields, and types available for custom queries against a given runnable type.
**How to use:**
- Start with section: "queryType" to see the root query fields available for this runnable type.
- Use section: "types" to list all types in the schema.
- Use section: "type" and provide a typeName (from the previous step) to get detailed information about a specific type, including its fields and arguments.
**Typical workflow:**
1. Call with just runnableTypeUri (defaults to section: "queryType") to see entry points.
2. Call with section: "types" to enumerate all types.
3. Call with section: "type" and a typeName to drill into a type's structure.
**Use this tool to:**
- Build custom queries for use with guardrails_query_runnable
- Understand the data model for a specific policy or control type
- Explore available fields, arguments, and relationships for advanced automation
`.trim(),
inputSchema: {
type: "object",
properties: {
runnableTypeUri: {
type: "string",
description: "The URI of the runnable type (policy or control type)"
},
section: {
type: "string",
description: "Start with 'queryType' to see available fields, then use 'types' to list all types, finally use 'type' to inspect a specific type",
enum: ["queryType", "types", "type"]
},
typeName: {
type: "string",
description: "Required when section is 'type': the name of a type discovered from the 'types' section"
}
},
required: ["runnableTypeUri"],
additionalProperties: false
},
handler: async ({ runnableTypeUri, section = "queryType", typeName }) => {
try {
// Construct the endpoint with query parameters
const endpoint = `/api/v5/graphql?runnableTypeUri=${encodeURIComponent(runnableTypeUri)}`;
if (section === "type" && !typeName) {
logger.error("Missing required typeName parameter for type section");
return errorResponse("typeName is required when section is 'type'. First use section: 'types' to discover available type names.");
}
const query = section === "type" ? TYPE_DETAILS_QUERY : ROOT_SCHEMA_QUERY;
const variables = section === "type" ? { typeName } : {};
const result = await executeQuery(query, variables, endpoint);
// Parse the JSON string returned by executeQuery
const data = JSON.parse(result);
// Filter the response based on the requested section
let filteredData;
let guidance = "";
if (section === "queryType") {
filteredData = data.__schema.queryType;
guidance = "These are the available fields at the root of the schema. To explore a type in detail, first use section: 'types' to list all types.";
}
else if (section === "types") {
filteredData = data.__schema.types;
guidance = "These are all available types in the schema. To explore a specific type, use section: 'type' with typeName set to one of these type names.";
}
else {
filteredData = data.__type;
guidance = "This is the detailed information for the requested type.";
}
return {
content: [
{
type: "text",
text: guidance
},
{
type: "text",
text: formatJsonToolResponse(filteredData).content[0].text
}
]
};
}
catch (error) {
logger.error(`Error executing introspection query: ${error.message}`);
return errorResponse(error.message);
}
}
};