From 627023ffccdd771cb2840b6fadac0a403b220b77 Mon Sep 17 00:00:00 2001 From: "a.tolmachev" Date: Sun, 5 Apr 2026 22:40:52 +0300 Subject: [PATCH] docs: add public smoke configs for mcp checks --- README.md | 1 + TASKS.md | 12 +- docs/public-smoke-targets.md | 169 ++++++++++++++++++ .../graphql-countries.operation.json | 131 ++++++++++++++ .../graphql-countries.test-input.json | 3 + .../mcp-smoke/grpc-grpcb-add.operation.json | 100 +++++++++++ .../mcp-smoke/grpc-grpcb-add.test-input.json | 4 + examples/mcp-smoke/grpc-grpcb-addsvc.proto | 28 +++ .../mcp-smoke/rest-open-meteo.operation.json | 120 +++++++++++++ .../mcp-smoke/rest-open-meteo.test-input.json | 4 + 10 files changed, 566 insertions(+), 6 deletions(-) create mode 100644 docs/public-smoke-targets.md create mode 100644 examples/mcp-smoke/graphql-countries.operation.json create mode 100644 examples/mcp-smoke/graphql-countries.test-input.json create mode 100644 examples/mcp-smoke/grpc-grpcb-add.operation.json create mode 100644 examples/mcp-smoke/grpc-grpcb-add.test-input.json create mode 100644 examples/mcp-smoke/grpc-grpcb-addsvc.proto create mode 100644 examples/mcp-smoke/rest-open-meteo.operation.json create mode 100644 examples/mcp-smoke/rest-open-meteo.test-input.json diff --git a/README.md b/README.md index a3eb932..60290f9 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Crank - платформа для публикации внешних API в в - `docs/runtime-config.md` - конфигурация окружения. - `docs/deployment.md` - деплой, reverse proxy и CI/CD. - `docs/demo-runbook.md` - демонстрационный сценарий. +- `docs/public-smoke-targets.md` - готовые публичные upstream-сервисы и payload-ы для smoke-проверки MCP. - `docs/rust-design.md` - правила распределения поведения в Rust. - `docs/development-rules.md` - правила разработки и workflow. - `docs/rust-code-rules.md` - Rust-specific coding rules. diff --git a/TASKS.md b/TASKS.md index 30de76e..04e1f6f 100644 --- a/TASKS.md +++ b/TASKS.md @@ -2,19 +2,19 @@ ## Current -### `feat/frontend-e2e` +### `feat/public-smoke-configs` Status: completed DoD: -- Playwright covers the core UI journeys against a real local stack -- Frontend e2e can be run locally without manual environment setup -- CI executes frontend e2e as a separate job and publishes failure artifacts +- Public REST, GraphQL, and gRPC smoke targets are documented +- Repository contains ready-to-use operation payloads for MCP verification +- gRPC smoke config includes a runtime-ready descriptor set ## Next -- `feat/alpine-polish` +- `feat/manual-regression-pass` ## Backlog -- `feat/alpine-polish` +- `feat/manual-regression-pass` diff --git a/docs/public-smoke-targets.md b/docs/public-smoke-targets.md new file mode 100644 index 0000000..1a4508c --- /dev/null +++ b/docs/public-smoke-targets.md @@ -0,0 +1,169 @@ +# Public Smoke Targets + +Этот документ фиксирует три публичных upstream-сервиса, которые можно использовать для ручной проверки `REST`, `GraphQL` и `gRPC` operation в Crank без поднятия своих тестовых backend-ов. + +Все примеры ниже уже сверены с реальными публичными endpoint-ами и дублируются готовыми payload-файлами в [examples/mcp-smoke](/home/a.tolmachev/code/rust/mcpaas/examples/mcp-smoke). + +## 1. Источники + +- REST: Open-Meteo Weather Forecast API + `https://open-meteo.com/en/docs` +- GraphQL: Countries GraphQL API + `https://github.com/trevorblades/countries/blob/main/README.md` + Playground / endpoint: `https://countries.trevorblades.com/` +- gRPC: grpcb.in + `https://grpcb.in/` + Public proto: `https://raw.githubusercontent.com/moul/pb/master/addsvc/addsvc.proto` + +## 2. Готовые operation payload-ы + +- REST: [rest-open-meteo.operation.json](/home/a.tolmachev/code/rust/mcpaas/examples/mcp-smoke/rest-open-meteo.operation.json) +- REST test input: [rest-open-meteo.test-input.json](/home/a.tolmachev/code/rust/mcpaas/examples/mcp-smoke/rest-open-meteo.test-input.json) +- GraphQL: [graphql-countries.operation.json](/home/a.tolmachev/code/rust/mcpaas/examples/mcp-smoke/graphql-countries.operation.json) +- GraphQL test input: [graphql-countries.test-input.json](/home/a.tolmachev/code/rust/mcpaas/examples/mcp-smoke/graphql-countries.test-input.json) +- gRPC: [grpc-grpcb-add.operation.json](/home/a.tolmachev/code/rust/mcpaas/examples/mcp-smoke/grpc-grpcb-add.operation.json) +- gRPC test input: [grpc-grpcb-add.test-input.json](/home/a.tolmachev/code/rust/mcpaas/examples/mcp-smoke/grpc-grpcb-add.test-input.json) +- gRPC proto reference: [grpc-grpcb-addsvc.proto](/home/a.tolmachev/code/rust/mcpaas/examples/mcp-smoke/grpc-grpcb-addsvc.proto) + +## 3. Как использовать + +### 3.1. Через UI + +1. Создай operation вручную в `Wizard`. +2. Подставь значения из соответствующего `*.operation.json`. +3. Для `gRPC` можно либо: + - просто использовать готовый `descriptor_set_b64` из JSON payload; + - либо отдельно загрузить `.proto` из [grpc-grpcb-addsvc.proto](/home/a.tolmachev/code/rust/mcpaas/examples/mcp-smoke/grpc-grpcb-addsvc.proto). +4. На шаге теста используй `*.test-input.json`. + +### 3.2. Через admin-api + +Пример для `ws_default`: + +```bash +curl -sS -X POST \ + https://rmcp.itexp.me/api/admin/workspaces/ws_default/operations \ + -H 'content-type: application/json' \ + -b cookie.txt \ + --data @examples/mcp-smoke/rest-open-meteo.operation.json +``` + +Потом test-run: + +```bash +curl -sS -X POST \ + https://rmcp.itexp.me/api/admin/workspaces/ws_default/operations//test-runs \ + -H 'content-type: application/json' \ + -b cookie.txt \ + --data '{ + "version": 1, + "input": '"$(cat examples/mcp-smoke/rest-open-meteo.test-input.json)"' + }' +``` + +Логин перед этим: + +```bash +curl -sS -c cookie.txt \ + -H 'content-type: application/json' \ + -X POST https://rmcp.itexp.me/api/auth/login \ + --data '{"email":"","password":""}' +``` + +## 4. Что именно проверяют примеры + +### 4.1. REST: Open-Meteo + +- Protocol: `REST` +- Endpoint: `https://api.open-meteo.com/v1/forecast` +- Проверка: + - query mapping + - fixed query defaults + - JSON response extraction + +Ожидаемый upstream response shape: + +```json +{ + "timezone": "Europe/Moscow", + "current": { + "time": "2026-04-05T22:30", + "temperature_2m": 3.4, + "wind_speed_10m": 8.3 + } +} +``` + +### 4.2. GraphQL: Countries + +- Protocol: `GraphQL` +- Endpoint: `https://countries.trevorblades.com/` +- Проверка: + - fixed GraphQL query + - variable mapping + - `response_path` + - nested array extraction + +Ожидаемый upstream query: + +```graphql +query GetCountry($code: ID!) { + country(code: $code) { + code + name + capital + emoji + currency + languages { + code + name + } + } +} +``` + +### 4.3. gRPC: grpcb.in / addsvc.Add.Sum + +- Protocol: `gRPC` +- Endpoint: `http://grpcb.in:9000` +- Service: `addsvc.Add` +- Method: `Sum` +- Проверка: + - descriptor-backed unary gRPC call + - `request.grpc.*` mapping + - protobuf JSON response normalization + - `to_number` transform from protobuf `int64` string + +Проверенный реальный вызов: + +```bash +docker run --rm fullstorydev/grpcurl:latest \ + -plaintext \ + -d '{"a":7,"b":5}' \ + grpcb.in:9000 \ + addsvc.Add/Sum +``` + +Ожидаемый ответ: + +```json +{ + "v": "12" +} +``` + +## 5. Практическая рекомендация + +Для первого smoke pass используй именно эти три operation: + +- `weather_current_open_meteo` +- `country_lookup_countries` +- `grpcb_add_sum` + +Этого достаточно, чтобы проверить весь путь: + +- create operation +- test-run +- publish +- bind to agent +- MCP call через `workspace + agent` diff --git a/examples/mcp-smoke/graphql-countries.operation.json b/examples/mcp-smoke/graphql-countries.operation.json new file mode 100644 index 0000000..edbb393 --- /dev/null +++ b/examples/mcp-smoke/graphql-countries.operation.json @@ -0,0 +1,131 @@ +{ + "name": "country_lookup_countries", + "display_name": "Lookup Country", + "category": "geo", + "protocol": "graphql", + "target": { + "kind": "graphql", + "endpoint": "https://countries.trevorblades.com/", + "operation_type": "query", + "operation_name": "GetCountry", + "query_template": "query GetCountry($code: ID!) { country(code: $code) { code name capital emoji currency languages { code name } } }", + "response_path": "$.response.body.data.country" + }, + "input_schema": { + "type": "object", + "required": true, + "fields": { + "code": { + "type": "string", + "required": true + } + } + }, + "output_schema": { + "type": "object", + "required": true, + "fields": { + "code": { + "type": "string", + "required": true + }, + "name": { + "type": "string", + "required": true + }, + "capital": { + "type": "string", + "required": false + }, + "emoji": { + "type": "string", + "required": true + }, + "currency": { + "type": "string", + "required": false + }, + "languages": { + "type": "array", + "required": true, + "items": { + "type": "object", + "required": true, + "fields": { + "code": { + "type": "string", + "required": true + }, + "name": { + "type": "string", + "required": true + } + } + } + } + } + }, + "input_mapping": { + "rules": [ + { + "source": "$.mcp.code", + "target": "$.request.variables.code", + "required": true + } + ] + }, + "output_mapping": { + "rules": [ + { + "source": "$.response.data.code", + "target": "$.output.code", + "required": true + }, + { + "source": "$.response.data.name", + "target": "$.output.name", + "required": true + }, + { + "source": "$.response.data.capital", + "target": "$.output.capital", + "required": false + }, + { + "source": "$.response.data.emoji", + "target": "$.output.emoji", + "required": true + }, + { + "source": "$.response.data.currency", + "target": "$.output.currency", + "required": false + }, + { + "source": "$.response.data.languages", + "target": "$.output.languages", + "required": true + } + ] + }, + "execution_config": { + "timeout_ms": 10000, + "headers": {} + }, + "tool_description": { + "title": "Lookup Country", + "description": "Fetch country metadata from the public Countries GraphQL API.", + "tags": [ + "graphql", + "geo", + "smoke" + ], + "examples": [ + { + "input": { + "code": "BR" + } + } + ] + } +} diff --git a/examples/mcp-smoke/graphql-countries.test-input.json b/examples/mcp-smoke/graphql-countries.test-input.json new file mode 100644 index 0000000..4825ea8 --- /dev/null +++ b/examples/mcp-smoke/graphql-countries.test-input.json @@ -0,0 +1,3 @@ +{ + "code": "BR" +} diff --git a/examples/mcp-smoke/grpc-grpcb-add.operation.json b/examples/mcp-smoke/grpc-grpcb-add.operation.json new file mode 100644 index 0000000..d1c1739 --- /dev/null +++ b/examples/mcp-smoke/grpc-grpcb-add.operation.json @@ -0,0 +1,100 @@ +{ + "name": "grpcb_add_sum", + "display_name": "Add Numbers (grpcb.in)", + "category": "math", + "protocol": "grpc", + "target": { + "kind": "grpc", + "server_addr": "http://grpcb.in:9000", + "package": "addsvc", + "service": "Add", + "method": "Sum", + "descriptor_ref": "desc_addsvc_grpcb", + "descriptor_set_b64": "CqQKCgxhZGRzdmMucHJvdG8SBmFkZHN2YyIoCgpTdW1SZXF1ZXN0EgwKAWEYASABKANSAWESDAoBYhgCIAEoA1IBYiIqCghTdW1SZXBseRIMCgF2GAEgASgDUgF2EhAKA2VychgCIAEoCVIDZXJyIisKDUNvbmNhdFJlcXVlc3QSDAoBYRgBIAEoCVIBYRIMCgFiGAIgASgJUgFiIi0KC0NvbmNhdFJlcGx5EgwKAXYYASABKAlSAXYSEAoDZXJyGAIgASgJUgNlcnIybAoDQWRkEi0KA1N1bRISLmFkZHN2Yy5TdW1SZXF1ZXN0GhAuYWRkc3ZjLlN1bVJlcGx5IgASNgoGQ29uY2F0EhUuYWRkc3ZjLkNvbmNhdFJlcXVlc3QaEy5hZGRzdmMuQ29uY2F0UmVwbHkiAErjBwoGEgQAACMBCggKAQwSAwAAEgoICgECEgMCAA8KKQoCBgASBAUACwEaHSBUaGUgQWRkIHNlcnZpY2UgZGVmaW5pdGlvbi4KCgoKAwYAARIDBQgLCiEKBAYAAgASAwcCLBoUIFN1bXMgdHdvIGludGVnZXJzLgoKDAoFBgACAAESAwcGCQoMCgUGAAIAAhIDBwsVCgwKBQYAAgADEgMHICgKJwoEBgACARIDCgI1GhogQ29uY2F0ZW5hdGVzIHR3byBzdHJpbmdzCgoMCgUGAAIBARIDCgYMCgwKBQYAAgECEgMKDhsKDAoFBgACAQMSAwomMQo2CgIEABIEDgARARoqIFRoZSBzdW0gcmVxdWVzdCBjb250YWlucyB0d28gcGFyYW1ldGVycy4KCgoKAwQAARIDDggSCgsKBAQAAgASAw8CDgoMCgUEAAIABRIDDwIHCgwKBQQAAgABEgMPCAkKDAoFBAACAAMSAw8MDQoLCgQEAAIBEgMQAg4KDAoFBAACAQUSAxACBwoMCgUEAAIBARIDEAgJCgwKBQQAAgEDEgMQDA0KRgoCBAESBBQAFwEaOiBUaGUgc3VtIHJlc3BvbnNlIGNvbnRhaW5zIHRoZSByZXN1bHQgb2YgdGhlIGNhbGN1bGF0aW9uLgoKCgoDBAEBEgMUCBAKCwoEBAECABIDFQIOCgwKBQQBAgAFEgMVAgcKDAoFBAECAAESAxUICQoMCgUEAQIAAxIDFQwNCgsKBAQBAgESAxYCEQoMCgUEAQIBBRIDFgIICgwKBQQBAgEBEgMWCQwKDAoFBAECAQMSAxYPEAo5CgIEAhIEGgAdARotIFRoZSBDb25jYXQgcmVxdWVzdCBjb250YWlucyB0d28gcGFyYW1ldGVycy4KCgoKAwQCARIDGggVCgsKBAQCAgASAxsCDwoMCgUEAgIABRIDGwIICgwKBQQCAgABEgMbCQoKDAoFBAICAAMSAxsNDgoLCgQEAgIBEgMcAg8KDAoFBAICAQUSAxwCCAoMCgUEAgIBARIDHAkKCgwKBQQCAgEDEgMcDQ4KSwoCBAMSBCAAIwEaPyBUaGUgQ29uY2F0IHJlc3BvbnNlIGNvbnRhaW5zIHRoZSByZXN1bHQgb2YgdGhlIGNvbmNhdGVuYXRpb24uCgoKCgMEAwESAyAIEwoLCgQEAwIAEgMhAg8KDAoFBAMCAAUSAyECCAoMCgUEAwIAARIDIQkKCgwKBQQDAgADEgMhDQ4KCwoEBAMCARIDIgIRCgwKBQQDAgEFEgMiAggKDAoFBAMCAQESAyIJDAoMCgUEAwIBAxIDIg8QYgZwcm90bzM=" + }, + "input_schema": { + "type": "object", + "required": true, + "fields": { + "a": { + "type": "integer", + "required": true + }, + "b": { + "type": "integer", + "required": true + } + } + }, + "output_schema": { + "type": "object", + "required": true, + "fields": { + "sum": { + "type": "integer", + "required": true + }, + "error_message": { + "type": "string", + "required": false + } + } + }, + "input_mapping": { + "rules": [ + { + "source": "$.mcp.a", + "target": "$.request.grpc.a", + "required": true + }, + { + "source": "$.mcp.b", + "target": "$.request.grpc.b", + "required": true + } + ] + }, + "output_mapping": { + "rules": [ + { + "source": "$.response.data.v", + "target": "$.output.sum", + "required": true, + "transform": { + "kind": "to_number" + } + }, + { + "source": "$.response.data.err", + "target": "$.output.error_message", + "required": false + } + ] + }, + "execution_config": { + "timeout_ms": 5000, + "headers": {}, + "protocol_options": { + "grpc": { + "use_tls": false + } + } + }, + "tool_description": { + "title": "Add Numbers (grpcb.in)", + "description": "Call the public grpcb.in addsvc.Add/Sum unary RPC through Crank.", + "tags": [ + "grpc", + "math", + "smoke" + ], + "examples": [ + { + "input": { + "a": 7, + "b": 5 + } + } + ] + } +} diff --git a/examples/mcp-smoke/grpc-grpcb-add.test-input.json b/examples/mcp-smoke/grpc-grpcb-add.test-input.json new file mode 100644 index 0000000..e3c3aaf --- /dev/null +++ b/examples/mcp-smoke/grpc-grpcb-add.test-input.json @@ -0,0 +1,4 @@ +{ + "a": 7, + "b": 5 +} diff --git a/examples/mcp-smoke/grpc-grpcb-addsvc.proto b/examples/mcp-smoke/grpc-grpcb-addsvc.proto new file mode 100644 index 0000000..b6445b1 --- /dev/null +++ b/examples/mcp-smoke/grpc-grpcb-addsvc.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +package addsvc; + +service Add { + rpc Sum (SumRequest) returns (SumReply) {} + rpc Concat (ConcatRequest) returns (ConcatReply) {} +} + +message SumRequest { + int64 a = 1; + int64 b = 2; +} + +message SumReply { + int64 v = 1; + string err = 2; +} + +message ConcatRequest { + string a = 1; + string b = 2; +} + +message ConcatReply { + string v = 1; + string err = 2; +} diff --git a/examples/mcp-smoke/rest-open-meteo.operation.json b/examples/mcp-smoke/rest-open-meteo.operation.json new file mode 100644 index 0000000..9e9505e --- /dev/null +++ b/examples/mcp-smoke/rest-open-meteo.operation.json @@ -0,0 +1,120 @@ +{ + "name": "weather_current_open_meteo", + "display_name": "Get Current Weather", + "category": "weather", + "protocol": "rest", + "target": { + "kind": "rest", + "base_url": "https://api.open-meteo.com", + "method": "GET", + "path_template": "/v1/forecast", + "static_headers": {} + }, + "input_schema": { + "type": "object", + "required": true, + "fields": { + "latitude": { + "type": "number", + "required": true + }, + "longitude": { + "type": "number", + "required": true + } + } + }, + "output_schema": { + "type": "object", + "required": true, + "fields": { + "time": { + "type": "string", + "required": true + }, + "temperature_c": { + "type": "number", + "required": true + }, + "wind_speed_kmh": { + "type": "number", + "required": true + }, + "timezone": { + "type": "string", + "required": true + } + } + }, + "input_mapping": { + "rules": [ + { + "source": "$.mcp.latitude", + "target": "$.request.query.latitude", + "required": true + }, + { + "source": "$.mcp.longitude", + "target": "$.request.query.longitude", + "required": true + }, + { + "source": "$.mcp.current", + "target": "$.request.query.current", + "required": false, + "default_value": "temperature_2m,wind_speed_10m" + }, + { + "source": "$.mcp.timezone", + "target": "$.request.query.timezone", + "required": false, + "default_value": "Europe/Moscow" + } + ] + }, + "output_mapping": { + "rules": [ + { + "source": "$.response.body.current.time", + "target": "$.output.time", + "required": true + }, + { + "source": "$.response.body.current.temperature_2m", + "target": "$.output.temperature_c", + "required": true + }, + { + "source": "$.response.body.current.wind_speed_10m", + "target": "$.output.wind_speed_kmh", + "required": true + }, + { + "source": "$.response.body.timezone", + "target": "$.output.timezone", + "required": true + } + ] + }, + "execution_config": { + "timeout_ms": 10000, + "headers": {} + }, + "tool_description": { + "title": "Get Current Weather", + "description": "Fetch current weather data from Open-Meteo by latitude and longitude.", + "tags": [ + "weather", + "rest", + "smoke" + ], + "examples": [ + { + "input": { + "latitude": 55.75, + "longitude": 37.62 + } + } + ] + } +} diff --git a/examples/mcp-smoke/rest-open-meteo.test-input.json b/examples/mcp-smoke/rest-open-meteo.test-input.json new file mode 100644 index 0000000..ddb10e4 --- /dev/null +++ b/examples/mcp-smoke/rest-open-meteo.test-input.json @@ -0,0 +1,4 @@ +{ + "latitude": 55.75, + "longitude": 37.62 +}