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.
Prerequisites​
- The first MCP endpoint tutorial completed against reShapr
0.2.3 - reShapr CLI
0.2.3, authenticated against that environment curlandjq- A running 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 broad generated get_v1_forecast Tool 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 allow only the agent-oriented current_weather Tool. The baseline Plan selects the Custom Tool; the filtered Plan selects the same Tool and 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 '["current_weather"]' \
--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 '["current_weather"]' \
--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":"0.2.3"},"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":"0.2.3"},"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 '["current_weather"]' \
--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
0.2.3, 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.