Context Control in Practice
Start from the Open-Meteo Service created in the first endpoint tutorial. You will attach a declarative Custom Tool and an output filter, expose two Plans, and compare the same live weather call before and after filtering.
See Context Control applied to GitHub GraphQL and the resulting surface used by an MCP App. The examples use a different API; the tutorial below remains the reproducible 1.1.0 procedure.
Prerequisites​
- The first MCP endpoint tutorial completed against reShapr
1.1.0 - reShapr CLI
1.1.0, authenticated against that environment curlandjq- A running proxy registered as a Gateway in Gateway Group
1
The example endpoint is intentionally unauthenticated. Add the authentication header required by your Exposition if you apply the procedure elsewhere.
Locate the Open-Meteo Service​
Use structured CLI output to select the Service imported by the first tutorial:
RESHAPR_SERVICE_ID="$(
reshapr service list --output json \
| jq -er 'map(select(.name == "Open-Meteo Weather Forecast API" and .version == "1.0")) | first | .id'
)"
export RESHAPR_SERVICE_ID
Attach an agent-oriented Tool​
The sample Custom Tool definition maps the canonical GET /v1/forecast operation, exposed by default as get_v1_forecast, to one action named current_weather. Its three inputs fix the requested weather fields while keeping the location explicit.
Attach it and retain the Artifact name returned by reShapr:
CUSTOM_ARTIFACT_NAME="$(
reshapr attach \
--url 'https://reshapr.io/examples/context-control/open-meteo-current-weather.yaml' \
--output json \
| jq -er '.name'
)"
export CUSTOM_ARTIFACT_NAME
Inspect the capabilities derived during attachment:
reshapr artifact list --serviceId "$RESHAPR_SERVICE_ID"
The CAPS count for this Artifact is 1. Its capability is the current_weather Tool name; use reshapr artifact get <artifact-id> to display it.
Attach an output filter​
The sample output filter retains the location, timezone, units, and current conditions while removing forecast metadata that this action does not need.
FILTER_ARTIFACT_NAME="$(
reshapr attach \
--url 'https://reshapr.io/examples/context-control/open-meteo-current-weather-filter.yaml' \
--output json \
| jq -er '.name'
)"
export FILTER_ARTIFACT_NAME
Create two bounded Plans​
Both Plans select only the canonical GET /v1/forecast operation. The Custom Tool replaces it with the agent-oriented current_weather Tool; the filtered Plan also selects its output filter.
BASELINE_ARTIFACTS="$(jq -cn --arg custom "$CUSTOM_ARTIFACT_NAME" '[$custom]')"
FILTERED_ARTIFACTS="$(
jq -cn \
--arg custom "$CUSTOM_ARTIFACT_NAME" \
--arg filter "$FILTER_ARTIFACT_NAME" \
'[$custom, $filter]'
)"
BASELINE_PLAN_ID="$(
reshapr config create 'weather-action-baseline' \
--serviceId "$RESHAPR_SERVICE_ID" \
--backendEndpoint 'https://api.open-meteo.com' \
--includedOperations '["GET /v1/forecast"]' \
--includedArtifacts "$BASELINE_ARTIFACTS" \
--output json \
| jq -er '.id'
)"
FILTERED_PLAN_ID="$(
reshapr config create 'weather-action-filtered' \
--serviceId "$RESHAPR_SERVICE_ID" \
--backendEndpoint 'https://api.open-meteo.com' \
--includedOperations '["GET /v1/forecast"]' \
--includedArtifacts "$FILTERED_ARTIFACTS" \
--output json \
| jq -er '.id'
)"
export BASELINE_PLAN_ID FILTERED_PLAN_ID
unset BASELINE_ARTIFACTS FILTERED_ARTIFACTS
Verify that the selections differ:
reshapr config get "$BASELINE_PLAN_ID"
reshapr config get "$FILTERED_PLAN_ID"
Expose both Plans​
Choose the scheme used by your Gateway, then create two named Expositions. Online uses https; the local Compose Gateway uses http.
export MCP_SCHEME='https'
BASELINE_MCP_URL="${MCP_SCHEME}://$(
reshapr expo create \
--configuration "$BASELINE_PLAN_ID" \
--gateway-group 1 \
--name 'weather-action-baseline' \
--output json \
| jq -er '.endpoints[0]'
)"
FILTERED_MCP_URL="${MCP_SCHEME}://$(
reshapr expo create \
--configuration "$FILTERED_PLAN_ID" \
--gateway-group 1 \
--name 'weather-action-filtered' \
--output json \
| jq -er '.endpoints[0]'
)"
export BASELINE_MCP_URL FILTERED_MCP_URL
Verify the Tool surface​
Use the same stateless MCP request for both endpoints:
list_tools() {
curl --silent --show-error \
--header 'Content-Type: application/json' \
--header 'Accept: application/json, text/event-stream' \
--header 'MCP-Protocol-Version: 2026-07-28' \
--header 'Mcp-Method: tools/list' \
--data '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientInfo":{"name":"reshapr-docs","version":"1.1.0"},"io.modelcontextprotocol/clientCapabilities":{}}}}' \
"$1" | jq -r '.result.tools[].name'
}
list_tools "$BASELINE_MCP_URL"
list_tools "$FILTERED_MCP_URL"
Each endpoint must list only current_weather. The generated get_v1_forecast operation is hidden behind the selected action.
Call and measure both results​
Capture only the Tool content, outside the JSON-RPC envelope:
call_weather() {
curl --silent --show-error \
--header 'Content-Type: application/json' \
--header 'Accept: application/json, text/event-stream' \
--header 'MCP-Protocol-Version: 2026-07-28' \
--header 'Mcp-Method: tools/call' \
--header 'Mcp-Name: current_weather' \
--data '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"current_weather","arguments":{"latitude":"48.8566","longitude":"2.3522","timezone":"Europe/Paris"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientInfo":{"name":"reshapr-docs","version":"1.1.0"},"io.modelcontextprotocol/clientCapabilities":{}}}}' \
"$1" | jq -er '.result.content[0].text'
}
BASELINE_RESULT="$(call_weather "$BASELINE_MCP_URL")"
FILTERED_RESULT="$(call_weather "$FILTERED_MCP_URL")"
printf 'Baseline content: %s bytes\n' "$(printf '%s' "$BASELINE_RESULT" | wc -c | tr -d ' ')"
printf 'Filtered content: %s bytes\n' "$(printf '%s' "$FILTERED_RESULT" | wc -c | tr -d ' ')"
Inspect the fields that remain:
jq 'keys' <<<"$BASELINE_RESULT"
jq 'keys' <<<"$FILTERED_RESULT"
jq '.current' <<<"$FILTERED_RESULT"
The filtered result must contain only latitude, longitude, timezone, current_units, and current. Its byte count should be lower than the baseline for this request.
The measurement uses UTF-8 bytes from the decoded Tool text, excludes the JSON-RPC envelope, and omits the trailing newline. Weather values and exact byte counts vary over time, so record the observed values rather than treating them as a product-wide reduction ratio.
Enable TOON encoding​
Keep the JSON comparison above reproducible, then create a third Plan with a separate TOON output filter. It retains the same fields and applies convertToToon last.
TOON_FILTER_NAME="$(
reshapr attach \
--url 'https://reshapr.io/examples/context-control/open-meteo-current-weather-toon.yaml' \
--output json \
| jq -er '.name'
)"
TOON_ARTIFACTS="$(
jq -cn \
--arg custom "$CUSTOM_ARTIFACT_NAME" \
--arg filter "$TOON_FILTER_NAME" \
'[$custom, $filter]'
)"
TOON_PLAN_ID="$(
reshapr config create 'weather-action-toon' \
--serviceId "$RESHAPR_SERVICE_ID" \
--backendEndpoint 'https://api.open-meteo.com' \
--includedOperations '["GET /v1/forecast"]' \
--includedArtifacts "$TOON_ARTIFACTS" \
--output json \
| jq -er '.id'
)"
TOON_MCP_URL="${MCP_SCHEME}://$(
reshapr expo create \
--configuration "$TOON_PLAN_ID" \
--gateway-group 1 \
--name 'weather-action-toon' \
--output json \
| jq -er '.endpoints[0]'
)"
export TOON_MCP_URL
unset TOON_ARTIFACTS
Call the TOON Exposition with the same arguments and inspect the encoded Tool content:
TOON_RESULT="$(call_weather "$TOON_MCP_URL")"
printf 'TOON content: %s bytes\n' "$(printf '%s' "$TOON_RESULT" | wc -c | tr -d ' ')"
printf '%s\n' "$TOON_RESULT"
The output is TOON text rather than JSON. This confirms the encoding stage is active; its byte count is one observation for this response, not a universal compression or token-saving claim.
Result​
You replaced a broad generated operation with one agent-oriented Tool, selected different Artifact sets for the same Service, measured the effect of response filtering, and activated TOON encoding on a separate Exposition.
Limits​
- This comparison covers one Open-Meteo request and does not predict token usage or model quality for other data.
- Output filtering is not a security boundary. In reShapr
1.1.0, a filter failure returns the original Tool response. - The before/after field comparison uses JSON before TOON is enabled so the retained keys remain directly inspectable.
- Re-running the tutorial with the same Plan or Exposition names requires deleting or renaming the previous resources, including the TOON variants.
Next step​
- Attach and Select reShapr Artifacts for Prompts, Resources, Custom Tools, and filters.
- Context Control: Mechanisms and Trade-offs for choosing the appropriate control at each stage.
- Tools Output Filtering for JSON Patch and TOON syntax.