diff --git a/executable/persisted/tests/Test.js b/executable/persisted/tests/Test.js
index d86da41..a31259a 100644
--- a/executable/persisted/tests/Test.js
+++ b/executable/persisted/tests/Test.js
@@ -9,9 +9,6 @@ const {
testDescription = getTestDescription("snippets", __dirname);
-const requestsFile = path.join(path.dirname(__dirname), "operations.graphql");
-const requests = fs.readFileSync(requestsFile, "utf8").toString();
-
describe(testDescription, function () {
const tests = [
{
diff --git a/executable/prescribed/README.md b/executable/prescribed/README.md
new file mode 100644
index 0000000..040cfe1
--- /dev/null
+++ b/executable/prescribed/README.md
@@ -0,0 +1,141 @@
+# Prescribed Tools
+
+A **prescribed tool** is a tool that maps to a specific [GraphQL operation](https://spec.graphql.org/September2025/#sec-Language.Operations) in a [persisted document](https://spec.graphql.org/September2025/#sec-Persisted-Documents). The `@tool(prescribed:)` argument links the tool definition to an operation name in a persisted document. This approach provides a structured way to expose specific GraphQL operations as tools that can be called by AI models.
+
+This sample demonstrates how to use the `@tool` directive to create prescribed tools for MCP (Model Context Protocol).
+
+## Overview
+
+The sample implements a mock weather service API with a single operation: weather forecast for a city.
+
+The key feature demonstrated is how to create a prescribed tool that maps to a persisted GraphQL operation, allowing AI models to execute specific, well-defined queries.
+
+## Schema Structure
+
+The schema consists of:
+
+1. A main schema file (`index.graphql`) that defines:
+ - The GraphQL types for weather data
+ - The `@tool` directive that creates a prescribed tool
+ - The `@sdl` directive that includes the persisted operation
+
+2. An operations file (`operations.graphql`) that contains:
+ - A GraphQL operation that will be exposed as a tool
+ - Descriptions for the operation and its variables (recommended best practice)
+
+## How Prescribed Tools Work
+
+A prescribed tool is defined using the `@tool` directive with the `prescribed` argument pointing to a specific operation in a persisted document:
+
+```graphql
+schema
+ # Load the persisted operations document that contains the WeatherForecast operation
+ @sdl(
+ files: []
+ executables: [{ document: "operations.graphql", persist: true }]
+ )
+ # Define a prescribed tool that maps to the WeatherForecast operation in the persisted document
+ @tool(name: "weather-lookup", prescribed: "WeatherForecast") {
+ query: Query
+}
+```
+
+The operation in the persisted document should include descriptions ([new to GraphQL September 2025](https://spec.graphql.org/September2025/#Description)) to help AI models understand how to use the tool:
+
+```graphql
+"""
+Get detailed weather forecast for a specific city
+This operation provides a multi-day weather forecast including temperature, conditions, and other meteorological data
+"""
+query WeatherForecast(
+ """The name of the city to get weather forecast for"""
+ $city: String!,
+ """Number of days to forecast (1-7), defaults to 3 days"""
+ $days: Int = 3
+) {
+ weatherForecast(city: $city, days: $days) {
+ city {
+ name
+ country
+ timezone
+ }
+ forecast {
+ date
+ conditions
+ high {
+ celsius
+ fahrenheit
+ }
+ low {
+ celsius
+ fahrenheit
+ }
+ precipitation
+ humidity
+ windSpeed
+ windDirection
+ }
+ }
+}
+```
+
+## MCP Tool Description
+
+When deployed, this schema will expose a tool through the MCP endpoint. The tool's description and parameter information are derived from the GraphQL operation's descriptions:
+
+```json
+{
+ "name": "weather-lookup",
+ "description": "Get detailed weather forecast for a specific city. This operation provides a multi-day weather forecast including temperature, conditions, and other meteorological data",
+ "inputSchema": {
+ "type": "object",
+ "properties": {
+ "variables": {
+ "properties": {
+ "city": {
+ "description": "The name of the city to get weather forecast for",
+ "type": "string"
+ },
+ "days": {
+ "description": "Number of days to forecast (1-7), defaults to 3 days",
+ "type": "integer",
+ "default": 3
+ }
+ },
+ "required": ["city"],
+ "type": "object"
+ }
+ },
+ "required": ["variables"]
+ }
+}
+```
+
+## Using the Tool
+
+An AI model can use this tool by:
+
+1. Understanding the tool's purpose from the operation's description
+2. Providing the required variables (city name and optionally number of days)
+3. Receiving structured weather forecast data in response
+
+## Benefits of Prescribed Tools
+
+1. **Controlled Access**: Only specific, predefined operations are exposed as tools, providing fine-grained control over what AI models can execute.
+2. **Type Safety**: The GraphQL schema ensures that inputs are properly validated.
+3. **Clear Documentation**: Operation descriptions serve as both documentation for developers and instructions for AI models.
+4. **Versioning**: Operations can be versioned and updated independently of the tool definition.
+
+## Testing as an MCP Tool
+
+To test this as an MCP tool with AI models:
+
+1. Deploy the schema to StepZen using the command `stepzen deploy`
+2. [Connect Claude Desktop](https://modelcontextprotocol.io/docs/develop/connect-local-servers) to your StepZen MCP endpoint
+3. The tool will appear as `weather-lookup` and can be called by the AI model
+
+**Example**: Interaction between MCP and the Claude UI.
+
+
+
+
\ No newline at end of file
diff --git a/executable/prescribed/index.graphql b/executable/prescribed/index.graphql
new file mode 100644
index 0000000..726b8f1
--- /dev/null
+++ b/executable/prescribed/index.graphql
@@ -0,0 +1,185 @@
+schema
+ # Load the persisted operations document that contains the WeatherForecast operation
+ @sdl(
+ files: []
+ executables: [{ document: "operations.graphql", persist: true }]
+ )
+ # Define a prescribed tool that maps to the WeatherForecast operation in the persisted document
+ @tool(name: "weather-lookup", prescribed: "WeatherForecast") {
+ query: Query
+}
+
+type Query {
+ # Get weather forecast for a specific city
+ weatherForecast(city: String!, days: Int = 1): WeatherForecast
+ @value(
+ script: {
+ src: """
+ function weatherForecast() {
+ const cities = {
+ "new york": {
+ name: "New York",
+ country: "United States",
+ latitude: 40.7128,
+ longitude: -74.0060,
+ timezone: "America/New_York",
+ population: 8804190
+ },
+ "london": {
+ name: "London",
+ country: "United Kingdom",
+ latitude: 51.5074,
+ longitude: -0.1278,
+ timezone: "Europe/London",
+ population: 8982000
+ },
+ "tokyo": {
+ name: "Tokyo",
+ country: "Japan",
+ latitude: 35.6762,
+ longitude: 139.6503,
+ timezone: "Asia/Tokyo",
+ population: 13960000
+ },
+ "sydney": {
+ name: "Sydney",
+ country: "Australia",
+ latitude: -33.8688,
+ longitude: 151.2093,
+ timezone: "Australia/Sydney",
+ population: 5312000
+ }
+ };
+
+ // Default to 1 day if not specified or out of range
+ const daysToForecast = days < 1 || days > 7 ? 1 : days;
+
+ const normalizedCity = city.toLowerCase();
+ const cityData = cities[normalizedCity] || {
+ name: city,
+ country: "Unknown",
+ latitude: 0,
+ longitude: 0,
+ timezone: "UTC",
+ population: null
+ };
+
+ // Fixed forecast patterns for each city
+ const forecastPatterns = {
+ "new york": [
+ { conditions: "Partly Cloudy", high: 22, low: 15, precipitation: 20, humidity: 65, windSpeed: 12, windDirection: "NW" },
+ { conditions: "Sunny", high: 24, low: 16, precipitation: 5, humidity: 55, windSpeed: 8, windDirection: "W" },
+ { conditions: "Cloudy", high: 20, low: 14, precipitation: 30, humidity: 70, windSpeed: 15, windDirection: "NE" },
+ { conditions: "Rain", high: 18, low: 12, precipitation: 75, humidity: 85, windSpeed: 18, windDirection: "E" },
+ { conditions: "Partly Cloudy", high: 23, low: 16, precipitation: 15, humidity: 60, windSpeed: 10, windDirection: "SW" },
+ { conditions: "Sunny", high: 25, low: 17, precipitation: 0, humidity: 50, windSpeed: 7, windDirection: "W" },
+ { conditions: "Thunderstorm", high: 21, low: 15, precipitation: 90, humidity: 90, windSpeed: 25, windDirection: "S" }
+ ],
+ "london": [
+ { conditions: "Cloudy", high: 16, low: 10, precipitation: 40, humidity: 75, windSpeed: 14, windDirection: "SW" },
+ { conditions: "Rain", high: 15, low: 9, precipitation: 65, humidity: 80, windSpeed: 16, windDirection: "W" },
+ { conditions: "Partly Cloudy", high: 17, low: 11, precipitation: 25, humidity: 70, windSpeed: 12, windDirection: "NW" },
+ { conditions: "Cloudy", high: 16, low: 10, precipitation: 35, humidity: 75, windSpeed: 13, windDirection: "SW" },
+ { conditions: "Rain", high: 14, low: 8, precipitation: 70, humidity: 85, windSpeed: 18, windDirection: "W" },
+ { conditions: "Partly Cloudy", high: 18, low: 12, precipitation: 20, humidity: 65, windSpeed: 11, windDirection: "NW" },
+ { conditions: "Sunny", high: 19, low: 13, precipitation: 10, humidity: 60, windSpeed: 9, windDirection: "N" }
+ ],
+ "tokyo": [
+ { conditions: "Sunny", high: 26, low: 19, precipitation: 5, humidity: 60, windSpeed: 10, windDirection: "E" },
+ { conditions: "Partly Cloudy", high: 25, low: 18, precipitation: 15, humidity: 65, windSpeed: 12, windDirection: "SE" },
+ { conditions: "Cloudy", high: 23, low: 17, precipitation: 30, humidity: 70, windSpeed: 14, windDirection: "S" },
+ { conditions: "Rain", high: 22, low: 16, precipitation: 60, humidity: 80, windSpeed: 16, windDirection: "SW" },
+ { conditions: "Partly Cloudy", high: 24, low: 18, precipitation: 20, humidity: 65, windSpeed: 11, windDirection: "E" },
+ { conditions: "Sunny", high: 27, low: 20, precipitation: 5, humidity: 55, windSpeed: 9, windDirection: "NE" },
+ { conditions: "Cloudy", high: 24, low: 18, precipitation: 25, humidity: 68, windSpeed: 13, windDirection: "SE" }
+ ],
+ "sydney": [
+ { conditions: "Sunny", high: 28, low: 20, precipitation: 10, humidity: 60, windSpeed: 15, windDirection: "NE" },
+ { conditions: "Partly Cloudy", high: 27, low: 19, precipitation: 15, humidity: 65, windSpeed: 14, windDirection: "E" },
+ { conditions: "Sunny", high: 29, low: 21, precipitation: 5, humidity: 55, windSpeed: 13, windDirection: "NE" },
+ { conditions: "Partly Cloudy", high: 26, low: 19, precipitation: 20, humidity: 70, windSpeed: 16, windDirection: "SE" },
+ { conditions: "Cloudy", high: 24, low: 18, precipitation: 35, humidity: 75, windSpeed: 18, windDirection: "S" },
+ { conditions: "Rain", high: 22, low: 17, precipitation: 70, humidity: 85, windSpeed: 20, windDirection: "SW" },
+ { conditions: "Partly Cloudy", high: 25, low: 18, precipitation: 25, humidity: 70, windDirection: "W", windSpeed: 17 }
+ ]
+ };
+
+ // Get pattern for city or use default
+ const pattern = forecastPatterns[normalizedCity] || forecastPatterns["new york"];
+
+ // Generate forecast for the requested number of days
+ const forecast = [];
+ const today = new Date();
+
+ for (let i = 0; i < daysToForecast; i++) {
+ const forecastDate = new Date();
+ forecastDate.setDate(today.getDate() + i);
+
+ const dayPattern = pattern[i % pattern.length];
+
+ forecast.push({
+ date: forecastDate.toISOString().split('T')[0],
+ sunrise: "06:25 AM",
+ sunset: "06:35 PM",
+ high: {
+ celsius: dayPattern.high,
+ fahrenheit: Math.round(dayPattern.high * 9 / 5 + 32)
+ },
+ low: {
+ celsius: dayPattern.low,
+ fahrenheit: Math.round(dayPattern.low * 9 / 5 + 32)
+ },
+ conditions: dayPattern.conditions,
+ precipitation: dayPattern.precipitation,
+ humidity: dayPattern.humidity,
+ windSpeed: dayPattern.windSpeed,
+ windDirection: dayPattern.windDirection
+ });
+ }
+
+ return {
+ city: cityData,
+ forecast: forecast,
+ };
+ }
+ weatherForecast()
+ """
+ }
+ )
+}
+
+type WeatherForecast {
+ city: City!
+ forecast: [DailyForecast!]!
+ lastUpdated: DateTime
+}
+
+type City {
+ name: String!
+ country: String!
+ latitude: Float!
+ longitude: Float!
+ timezone: String!
+ population: Int
+}
+
+type DailyForecast {
+ date: Date!
+ sunrise: String!
+ sunset: String!
+ high: Temperature!
+ low: Temperature!
+ conditions: String!
+ precipitation: Float!
+ humidity: Int!
+ windSpeed: Float!
+ windDirection: String!
+}
+
+type Temperature {
+ celsius: Float!
+ fahrenheit: Float!
+}
+
+scalar Date
+scalar DateTime
diff --git a/executable/prescribed/operations.graphql b/executable/prescribed/operations.graphql
new file mode 100644
index 0000000..a220226
--- /dev/null
+++ b/executable/prescribed/operations.graphql
@@ -0,0 +1,33 @@
+"""
+Get detailed weather forecast for a specific city
+This operation provides a multi-day weather forecast including temperature, conditions, and other meteorological data
+"""
+query WeatherForecast(
+ """The name of the city to get weather forecast for"""
+ $city: String!,
+ """Number of days to forecast (1-7), defaults to current day"""
+ $days: Int = 1
+) {
+ weatherForecast(city: $city, days: $days) {
+ city {
+ name
+ country
+ timezone
+ }
+ forecast {
+ high {
+ celsius
+ fahrenheit
+ }
+ low {
+ celsius
+ fahrenheit
+ }
+ conditions
+ precipitation
+ humidity
+ windSpeed
+ windDirection
+ }
+ }
+}
diff --git a/executable/prescribed/stepzen.config.json b/executable/prescribed/stepzen.config.json
new file mode 100644
index 0000000..af1c0ea
--- /dev/null
+++ b/executable/prescribed/stepzen.config.json
@@ -0,0 +1,3 @@
+{
+ "endpoint": "api/miscellaneous"
+}
diff --git a/executable/prescribed/tests/Test.js b/executable/prescribed/tests/Test.js
new file mode 100644
index 0000000..2b8819a
--- /dev/null
+++ b/executable/prescribed/tests/Test.js
@@ -0,0 +1,48 @@
+const {
+ deployAndRun,
+ stepzen,
+ getTestDescription,
+} = require("../../../tests/gqltest.js");
+
+testDescription = getTestDescription("snippets", __dirname);
+
+describe(testDescription, function () {
+ const tests = [
+ {
+ label: "WeatherForecast",
+ documentId:
+ "sha256:eb01da571734ea3074e4aa494398f947ff76c3b339ad0c15d5c9127b5f53ac4d",
+ operationName: "WeatherForecast",
+ variables: {
+ city: "Sydney",
+ },
+ expected: {
+ weatherForecast: {
+ city: {
+ name: "Sydney",
+ country: "Australia",
+ timezone: "Australia/Sydney"
+ },
+ forecast: [
+ {
+ high: {
+ celsius: 28,
+ fahrenheit: 82
+ },
+ low: {
+ celsius: 20,
+ fahrenheit: 68
+ },
+ conditions: "Sunny",
+ precipitation: 10,
+ humidity: 60,
+ windSpeed: 15,
+ windDirection: "NE"
+ },
+ ],
+ },
+ },
+ },
+ ];
+ return deployAndRun(__dirname, tests, stepzen.admin);
+});