Skip to content

Commit 76d1714

Browse files
authored
feat: add HttpCall block executor for HTTP/REST API integration (#16)
Implement HttpCall block type for making HTTP/REST API calls within workflows with environment variable substitution support. **New Components:** - HttpCallExecutor with httpx integration - Environment variable substitution (${ENV_VAR}) for URL, headers - Support for all HTTP methods (GET, POST, PUT, DELETE, PATCH, etc.) - JSON and text request bodies - Comprehensive response parsing (status_code, body, headers, parsed JSON) **Features:** - Timeout configuration (1-300 seconds, default: 30) - SSL verification control - HTTP redirect following - Success flag for 2xx status codes **Testing:** - Integration test with httpbin.org - Snapshot-based testing with dynamic field normalization - HTTP date header normalization - Amazon trace ID normalization - All 69 tests passing **Documentation:** - Updated README.md with HttpCall block type - Updated schema.json with complete HttpCall definition - Test workflow: tests/workflows/core/http-operations/basic-http-call.yaml 🤖 Generated with [Claude Code](https://claude.com/claude-code)
2 parents cfb3e90 + 52dc66c commit 76d1714

33 files changed

Lines changed: 1167 additions & 524 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ Control recursion depth with `WORKFLOWS_MAX_RECURSION_DEPTH` (default: 50, max:
195195
- **CreateFile** - Create files with content
196196
- **ReadFile** - Read file contents
197197
- **RenderTemplate** - Process Jinja2 templates
198+
- **HttpCall** - Make HTTP/REST API calls with environment variable substitution
198199
- **Prompt** - Interactive user prompts (with pause/resume)
199200
- **ReadJSONState** / **WriteJSONState** / **MergeJSONState** - Manage JSON state files
200201

schema.json

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
"CreateFile",
7878
"ReadFile",
7979
"RenderTemplate",
80+
"HttpCall",
8081
"Prompt",
8182
"ReadJSONState",
8283
"WriteJSONState",
@@ -427,6 +428,96 @@
427428
}
428429
}
429430
},
431+
{
432+
"if": {
433+
"properties": {
434+
"type": {
435+
"const": "HttpCall"
436+
}
437+
}
438+
},
439+
"then": {
440+
"properties": {
441+
"inputs": {
442+
"additionalProperties": false,
443+
"description": "Inputs for HttpCall block",
444+
"properties": {
445+
"url": {
446+
"description": "Request URL (supports ${ENV_VAR} substitution)",
447+
"title": "Url",
448+
"type": "string"
449+
},
450+
"method": {
451+
"default": "POST",
452+
"description": "HTTP method (GET, POST, PUT, DELETE, PATCH, etc.)",
453+
"title": "Method",
454+
"type": "string"
455+
},
456+
"headers": {
457+
"additionalProperties": {
458+
"type": "string"
459+
},
460+
"description": "HTTP headers (supports ${ENV_VAR} substitution in values)",
461+
"title": "Headers",
462+
"type": "object"
463+
},
464+
"body_json": {
465+
"anyOf": [
466+
{
467+
"additionalProperties": true,
468+
"type": "object"
469+
},
470+
{
471+
"type": "null"
472+
}
473+
],
474+
"default": null,
475+
"description": "JSON request body (mutually exclusive with body_text)",
476+
"title": "Body Json"
477+
},
478+
"body_text": {
479+
"anyOf": [
480+
{
481+
"type": "string"
482+
},
483+
{
484+
"type": "null"
485+
}
486+
],
487+
"default": null,
488+
"description": "Text request body (mutually exclusive with body_json)",
489+
"title": "Body Text"
490+
},
491+
"timeout": {
492+
"default": 30,
493+
"description": "Request timeout in seconds",
494+
"maximum": 300,
495+
"minimum": 1,
496+
"title": "Timeout",
497+
"type": "integer"
498+
},
499+
"follow_redirects": {
500+
"default": true,
501+
"description": "Whether to follow HTTP redirects",
502+
"title": "Follow Redirects",
503+
"type": "boolean"
504+
},
505+
"verify_ssl": {
506+
"default": true,
507+
"description": "Whether to verify SSL certificates",
508+
"title": "Verify Ssl",
509+
"type": "boolean"
510+
}
511+
},
512+
"required": [
513+
"url"
514+
],
515+
"title": "HttpCallInput",
516+
"type": "object"
517+
}
518+
}
519+
}
520+
},
430521
{
431522
"if": {
432523
"properties": {
@@ -820,6 +911,83 @@
820911
"title": "RenderTemplateInput",
821912
"type": "object"
822913
},
914+
"HttpCallInput": {
915+
"additionalProperties": false,
916+
"description": "Input model for HttpCall executor.",
917+
"properties": {
918+
"url": {
919+
"description": "Request URL (supports ${ENV_VAR} substitution)",
920+
"title": "Url",
921+
"type": "string"
922+
},
923+
"method": {
924+
"default": "POST",
925+
"description": "HTTP method (GET, POST, PUT, DELETE, PATCH, etc.)",
926+
"title": "Method",
927+
"type": "string"
928+
},
929+
"headers": {
930+
"additionalProperties": {
931+
"type": "string"
932+
},
933+
"description": "HTTP headers (supports ${ENV_VAR} substitution in values)",
934+
"title": "Headers",
935+
"type": "object"
936+
},
937+
"body_json": {
938+
"anyOf": [
939+
{
940+
"additionalProperties": true,
941+
"type": "object"
942+
},
943+
{
944+
"type": "null"
945+
}
946+
],
947+
"default": null,
948+
"description": "JSON request body (mutually exclusive with body_text)",
949+
"title": "Body Json"
950+
},
951+
"body_text": {
952+
"anyOf": [
953+
{
954+
"type": "string"
955+
},
956+
{
957+
"type": "null"
958+
}
959+
],
960+
"default": null,
961+
"description": "Text request body (mutually exclusive with body_json)",
962+
"title": "Body Text"
963+
},
964+
"timeout": {
965+
"default": 30,
966+
"description": "Request timeout in seconds",
967+
"maximum": 300,
968+
"minimum": 1,
969+
"title": "Timeout",
970+
"type": "integer"
971+
},
972+
"follow_redirects": {
973+
"default": true,
974+
"description": "Whether to follow HTTP redirects",
975+
"title": "Follow Redirects",
976+
"type": "boolean"
977+
},
978+
"verify_ssl": {
979+
"default": true,
980+
"description": "Whether to verify SSL certificates",
981+
"title": "Verify Ssl",
982+
"type": "boolean"
983+
}
984+
},
985+
"required": [
986+
"url"
987+
],
988+
"title": "HttpCallInput",
989+
"type": "object"
990+
},
823991
"PromptInput": {
824992
"additionalProperties": false,
825993
"description": "Input for Prompt executor.\n\nSimple design: single prompt field for maximum flexibility.",

src/workflows_mcp/engine/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from . import ( # noqa: F401
3636
executors_core, # Shell executor
3737
executors_file, # File operation executors
38+
executors_http, # HTTP/REST API executor
3839
executors_interactive, # Interactive executors
3940
executors_state, # JSON state executors
4041
executors_workflow, # Workflow executor (ADR-008)
@@ -60,6 +61,11 @@
6061
RenderTemplateInput,
6162
RenderTemplateOutput,
6263
)
64+
from .executors_http import (
65+
HttpCallExecutor,
66+
HttpCallInput,
67+
HttpCallOutput,
68+
)
6369
from .executors_interactive import (
6470
PromptExecutor,
6571
PromptInput,
@@ -116,6 +122,10 @@
116122
"RenderTemplateExecutor",
117123
"RenderTemplateInput",
118124
"RenderTemplateOutput",
125+
# HTTP Executors
126+
"HttpCallExecutor",
127+
"HttpCallInput",
128+
"HttpCallOutput",
119129
# Interactive Executors
120130
"PromptExecutor",
121131
"PromptInput",

src/workflows_mcp/engine/executor_base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@ def test_workflow():
586586
"""
587587
from .executors_core import ShellExecutor
588588
from .executors_file import CreateFileExecutor, ReadFileExecutor, RenderTemplateExecutor
589+
from .executors_http import HttpCallExecutor
589590
from .executors_interactive import PromptExecutor
590591
from .executors_state import (
591592
MergeJSONStateExecutor,
@@ -605,6 +606,9 @@ def test_workflow():
605606
registry.register(ReadFileExecutor())
606607
registry.register(RenderTemplateExecutor())
607608

609+
# Register HTTP executor
610+
registry.register(HttpCallExecutor())
611+
608612
# Register interactive executor
609613
registry.register(PromptExecutor())
610614

0 commit comments

Comments
 (0)