-
Notifications
You must be signed in to change notification settings - Fork 5
chore: add snippet for prescribed tool with descriptions #79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
asararatnakar
merged 3 commits into
stepzen-dev:main
from
asararatnakar:tools-with-descriptions
Nov 5, 2025
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. | ||
|
|
||
| <img width="563" height="360" alt="Image" src="https://github.com/user-attachments/assets/7da0cd20-2469-45cd-8a17-0891a1a03dec" /> | ||
|
|
||
| <img width="502" height="588" alt="Image" src="https://github.com/user-attachments/assets/cf499b7e-a3fe-433c-b837-bdde25441739" /> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| schema | ||
| # Load the persisted operations document that contains the WeatherForecast operation | ||
| @sdl( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a useful comment |
||
| 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") { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a useful comment |
||
| 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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "endpoint": "api/miscellaneous" | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
persisted document is not a GraphQL spec concept, at least not that spec, maybe a reference to the persisted snippet.