ApiLogicServer 14.3.25__py3-none-any.whl → 14.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. api_logic_server_cli/add_cust/add_cust.py +283 -0
  2. api_logic_server_cli/api_logic_server.py +18 -250
  3. api_logic_server_cli/api_logic_server_info.yaml +3 -3
  4. api_logic_server_cli/cli.py +54 -35
  5. api_logic_server_cli/create_from_model/__pycache__/api_logic_server_utils.cpython-312.pyc +0 -0
  6. api_logic_server_cli/create_from_model/__pycache__/create_db_from_model.cpython-312.pyc +0 -0
  7. api_logic_server_cli/create_from_model/__pycache__/dbml.cpython-312.pyc +0 -0
  8. api_logic_server_cli/create_from_model/__pycache__/ont_build.cpython-312.pyc +0 -0
  9. api_logic_server_cli/create_from_model/__pycache__/ont_create.cpython-312.pyc +0 -0
  10. api_logic_server_cli/create_from_model/api_logic_server_utils.py +47 -0
  11. api_logic_server_cli/create_from_model/create_db_from_model.py +2 -0
  12. api_logic_server_cli/create_from_model/dbml.py +113 -58
  13. api_logic_server_cli/create_from_model/ont_build.py +102 -74
  14. api_logic_server_cli/create_from_model/ont_create.py +7 -6
  15. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/.DS_Store +0 -0
  16. api_logic_server_cli/database/basic_demo.sqlite +0 -0
  17. api_logic_server_cli/database/basic_demo.txt +1 -0
  18. api_logic_server_cli/database/basic_demo_wg.sqlite +0 -0
  19. api_logic_server_cli/database/nw-gold-fix.sql +62 -0
  20. api_logic_server_cli/database/nw-gold.sqlite +0 -0
  21. api_logic_server_cli/{prototypes/manager/webgenai → fragments}/docker-compose.yml +1 -1
  22. api_logic_server_cli/genai/genai.py +42 -11
  23. api_logic_server_cli/genai/genai_graphics.py +252 -38
  24. api_logic_server_cli/genai/genai_svcs.py +20 -12
  25. api_logic_server_cli/manager.py +22 -12
  26. api_logic_server_cli/prototypes/.DS_Store +0 -0
  27. api_logic_server_cli/prototypes/base/.DS_Store +0 -0
  28. api_logic_server_cli/prototypes/base/.vscode/launch.json +22 -2
  29. api_logic_server_cli/prototypes/base/api/expose_api_models.py +3 -1
  30. api_logic_server_cli/prototypes/base/api_logic_server_run.py +5 -2
  31. api_logic_server_cli/prototypes/base/config/activate_logicbank.py +1 -0
  32. api_logic_server_cli/prototypes/base/config/config.py +123 -25
  33. api_logic_server_cli/prototypes/base/config/default.env +7 -1
  34. api_logic_server_cli/prototypes/base/config/logging.yml +1 -0
  35. api_logic_server_cli/prototypes/base/config/server_setup.py +33 -1
  36. api_logic_server_cli/prototypes/base/database/test_data/readme.md +5 -2
  37. api_logic_server_cli/prototypes/base/devops/docker-standard-image/docker-compose-standard-image.yml +7 -2
  38. api_logic_server_cli/prototypes/base/docs/training/logic_bank_api.prompt +314 -0
  39. api_logic_server_cli/prototypes/base/docs/training/logic_example.py +41 -0
  40. api_logic_server_cli/prototypes/base/integration/kafka/kafka_producer.py +12 -5
  41. api_logic_server_cli/prototypes/base/integration/n8n/n8n_producer.py +68 -21
  42. api_logic_server_cli/prototypes/base/integration/n8n/n8n_readme.md +19 -0
  43. api_logic_server_cli/prototypes/base/integration/system/FlaskKafka.py +5 -1
  44. api_logic_server_cli/prototypes/base/test/basic/server_test.py +1 -1
  45. api_logic_server_cli/prototypes/base/ui/templates/bar_chart.jinja +64 -0
  46. api_logic_server_cli/prototypes/basic_demo/README.md +29 -52
  47. api_logic_server_cli/prototypes/basic_demo/customizations/api/.DS_Store +0 -0
  48. api_logic_server_cli/prototypes/basic_demo/customizations/api/api_discovery/mcp_server_executor.py +138 -0
  49. api_logic_server_cli/prototypes/basic_demo/customizations/api/api_discovery/openapi.py +92 -0
  50. api_logic_server_cli/prototypes/basic_demo/customizations/api/api_discovery/proper_update_def.json +71 -0
  51. api_logic_server_cli/prototypes/basic_demo/customizations/config/default.env +13 -0
  52. api_logic_server_cli/prototypes/basic_demo/customizations/database/db.sqlite +0 -0
  53. api_logic_server_cli/prototypes/basic_demo/customizations/database/models.py +131 -0
  54. api_logic_server_cli/prototypes/basic_demo/customizations/integration/.DS_Store +0 -0
  55. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/.DS_Store +0 -0
  56. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/1_langchain_loader.py +71 -0
  57. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/2_gpt_mcp_prompt.txt +19 -0
  58. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/README_mcp.md +13 -0
  59. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/mcp_client_executor.py +295 -0
  60. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/mcp_schema.txt +47 -0
  61. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/mcp_server_discovery.json +9 -0
  62. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/multi_mcp_flow/multi_mcp_flow.png +0 -0
  63. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/multi_mcp_flow/multi_mcp_orchestration.yaml +49 -0
  64. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/multi_mcp_flow/wny mcp flows.png +0 -0
  65. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/natlang_to_api.py +73 -0
  66. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/curl.txt +5 -0
  67. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/images/MCP Overview.png +0 -0
  68. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/images/MCP_Arch.png +0 -0
  69. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/images/MCP_Overview_Executor.png +0 -0
  70. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/invoke_llm/1 - prompt_messages_array.json +10 -0
  71. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/invoke_llm/2 - completion_tool_context.json +12 -0
  72. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/llm_schema.txt +38 -0
  73. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/nw_swagger_2.yaml +17393 -0
  74. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/nw_swagger_3.yaml +16660 -0
  75. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/nw_swagger_3_relaxed.yaml +109 -0
  76. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/proxy_server.py +51 -0
  77. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/proxy_serverZ.py +72 -0
  78. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/validate_jsonapi.py +64 -0
  79. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/run_executor.py +23 -0
  80. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/swagger_converter.py +65 -0
  81. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/z_old/3_executor_test_agent.py +52 -0
  82. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/3_executor_test_agent.py +52 -0
  83. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/README_functon.md +201 -0
  84. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/ai_plugin.json +17 -0
  85. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/nw-swagger_3.json +1731 -0
  86. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/snippets.txt +5 -0
  87. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3 genai_demo_with_get.json +1731 -0
  88. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3.json +1782 -0
  89. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3_genai_demo.json +264 -0
  90. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3_genai_demo_with_update.json +1782 -0
  91. api_logic_server_cli/prototypes/basic_demo/customizations/logic/declare_logic.py +62 -44
  92. api_logic_server_cli/prototypes/basic_demo/customizations/security/declare_security.py +11 -12
  93. api_logic_server_cli/prototypes/basic_demo/customizations/ui/admin/admin.yaml +166 -0
  94. api_logic_server_cli/prototypes/basic_demo/iteration/api/{customize_api.py → api_discovery/order_b2b.py} +17 -23
  95. api_logic_server_cli/prototypes/basic_demo/iteration/database/db.sqlite +0 -0
  96. api_logic_server_cli/prototypes/basic_demo/iteration/integration/row_dict_maps/OrderB2B.py +6 -5
  97. api_logic_server_cli/prototypes/basic_demo/iteration/integration/row_dict_maps/OrderShipping.py +4 -4
  98. api_logic_server_cli/prototypes/basic_demo/iteration/logic/declare_logic.py +69 -43
  99. api_logic_server_cli/prototypes/basic_demo/iteration/ui/admin/admin.yaml +125 -50
  100. api_logic_server_cli/prototypes/genai_demo/ui/admin/admin.yaml +1 -1
  101. api_logic_server_cli/prototypes/manager/README.md +30 -4
  102. api_logic_server_cli/prototypes/manager/README_X.md +663 -0
  103. api_logic_server_cli/prototypes/manager/system/genai/.DS_Store +0 -0
  104. api_logic_server_cli/prototypes/manager/system/genai/examples/.DS_Store +0 -0
  105. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/.DS_Store +0 -0
  106. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.prompt +0 -10
  107. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +32 -10
  108. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_docs_logic/docs/002_create_db_models.prompt +4 -4
  109. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_docs_logic/docs/003_create_db_models.response +77 -47
  110. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_informal.prompt +1 -1
  111. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/dashboard_services.jinja +83 -0
  112. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_dashboard_WIP.py +34 -0
  113. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/{graphics_services.py → graphics_services_api_xxx.py} +0 -9
  114. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_services_db.jinja +46 -0
  115. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_services_db_each_method.jinja +36 -0
  116. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/graphics.prompt +7 -3
  117. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/response_format.prompt +8 -1
  118. api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/install-ApiLogicServer-dev.ps1 +100 -0
  119. api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/install-ApiLogicServer-dev.sh +116 -0
  120. api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/readme.md +7 -0
  121. api_logic_server_cli/prototypes/manager/system/style-guide.yaml +2 -2
  122. api_logic_server_cli/prototypes/manager/webgenai/README.md +6 -0
  123. api_logic_server_cli/prototypes/nw/docs/graphics/count_orders_by_category.prompt +1 -0
  124. api_logic_server_cli/prototypes/nw/docs/graphics/order_count_by_month.prompt +1 -0
  125. api_logic_server_cli/prototypes/nw/docs/graphics/request copy.json +892 -0
  126. api_logic_server_cli/prototypes/nw/docs/graphics/request.json +6 -0
  127. api_logic_server_cli/prototypes/nw/docs/graphics/response.json +17 -0
  128. api_logic_server_cli/prototypes/nw/docs/graphics/response.yaml +59 -0
  129. api_logic_server_cli/prototypes/nw/docs/graphics/sales_by_category.prompt +1 -0
  130. api_logic_server_cli/prototypes/nw/ui/admin/home.js +5 -4
  131. api_logic_server_cli/prototypes/nw/ui/app_model_custom.yaml +851 -1082
  132. api_logic_server_cli/prototypes/nw_no_cust/Tutorial.md +45 -26
  133. api_logic_server_cli/prototypes/nw_no_cust/api/api_discovery/openapi.py +130 -0
  134. api_logic_server_cli/prototypes/nw_no_cust/api/api_discovery/proper_update_def.json +71 -0
  135. api_logic_server_cli/prototypes/nw_no_cust/config/default.env +13 -0
  136. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/count_orders_by_category.prompt +1 -0
  137. api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/sales_by_employee.prompt +1 -0
  138. api_logic_server_cli/prototypes/ont_app/ontimize_seed/nginx/nginx.conf +2 -2
  139. api_logic_server_cli/prototypes/ont_app/ontimize_seed/package-lock.json +9725 -1180
  140. api_logic_server_cli/prototypes/ont_app/ontimize_seed/package.json +6 -9
  141. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/app/app.config.ts +2 -1
  142. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/app/shared/app.services.config.ts +1 -1
  143. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/assets/css/app.scss +4 -0
  144. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/assets/i18n/en.json +1 -1
  145. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/assets/i18n/es.json +14 -12
  146. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/environments/environment.prod.ts +5 -5
  147. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/environments/environment.ts +5 -5
  148. api_logic_server_cli/prototypes/ont_app/templates/app_config.jinja +1 -1
  149. api_logic_server_cli/prototypes/ont_app/templates/date_template.html +1 -1
  150. api_logic_server_cli/prototypes/ont_app/templates/detail_template.html +1 -1
  151. api_logic_server_cli/prototypes/ont_app/templates/new_template.html +16 -16
  152. api_logic_server_cli/prototypes/ont_app/templates/textarea_template.html +1 -1
  153. api_logic_server_cli/prototypes/ont_app/templates/timestamp_template.html +1 -1
  154. api_logic_server_cli/prototypes/sample_ai/logic/declare_logic.py +30 -13
  155. apilogicserver-14.5.0.dist-info/METADATA +76 -0
  156. {apilogicserver-14.3.25.dist-info → apilogicserver-14.5.0.dist-info}/RECORD +160 -88
  157. {apilogicserver-14.3.25.dist-info → apilogicserver-14.5.0.dist-info}/WHEEL +1 -1
  158. api_logic_server_cli/prototypes/basic_demo/apply_customizations.ps1 +0 -17
  159. api_logic_server_cli/prototypes/basic_demo/apply_customizations.sh +0 -14
  160. api_logic_server_cli/prototypes/basic_demo/apply_iteration.ps1 +0 -20
  161. api_logic_server_cli/prototypes/basic_demo/apply_iteration.sh +0 -15
  162. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/service_template_jsonapi_rpc.jinja +0 -37
  163. api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/service_template_unused.jinja +0 -38
  164. apilogicserver-14.3.25.dist-info/METADATA +0 -167
  165. {apilogicserver-14.3.25.dist-info → apilogicserver-14.5.0.dist-info}/entry_points.txt +0 -0
  166. {apilogicserver-14.3.25.dist-info → apilogicserver-14.5.0.dist-info}/licenses/LICENSE +0 -0
  167. {apilogicserver-14.3.25.dist-info → apilogicserver-14.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,71 @@
1
+ """
2
+ ==========================================================================================
3
+ LangChain OpenAPI Loader – JSON:API Planner Tool (1_langchain_loader.py)
4
+ ==========================================================================================
5
+
6
+ Alert:
7
+ ------
8
+ This does not work - hours of import version madness.
9
+
10
+
11
+ Purpose:
12
+ --------
13
+ This script loads an OpenAPI 3.0 specification (e.g., nw_swagger_3.yaml) using LangChain's
14
+ OpenAPI tools to dynamically generate a structured LangChain Tool. It can be used as a
15
+ building block in an AI system that automatically interprets natural language goals and
16
+ calls API endpoints by mapping them to OpenAPI operations.
17
+
18
+ How It's Used:
19
+ --------------
20
+ - Reads the OpenAPI schema and extracts available endpoints.
21
+ - Instantiates a tool for a specific operation (e.g., getCustomer).
22
+ - Allows GPT agents or scripts to call the tool using structured input.
23
+ - Typically used in multi-agent or planner + executor systems (e.g., AutoGen, LangGraph).
24
+ - It is *not* required to run integration/mcp/3_executor_test_agent.py
25
+
26
+ Limitations:
27
+ ------------
28
+ - This script does not itself parse natural language input.
29
+ - The operation_id must be known and match one in the OpenAPI spec.
30
+ - Requires LangChain and OpenAPI tool support installed.
31
+
32
+ Dependencies:
33
+ -------------
34
+ - langchain
35
+ - openapi-spec-validator
36
+
37
+ Recommended Next Step:
38
+ ----------------------
39
+ Use this in conjunction with a GPT planner that:
40
+ 1. Converts natural language into structured intent.
41
+ 2. Selects the correct operation_id.
42
+ 3. Provides parameters as expected by the OpenAPI operation.
43
+
44
+ """
45
+
46
+ from langchain.tools.openapi.utils.openapi_utils import OpenAPISpec
47
+ from langchain.tools.openapi.tool import OpenAPITool
48
+
49
+ # Load your OpenAPI spec
50
+ spec = OpenAPISpec.from_file("resources/nw_swagger_3.yaml")
51
+ spec = OpenAPISpec.from_file("resources/nw_swagger_2.yaml")
52
+
53
+ # List available operation IDs (optional debugging)
54
+ print("Available operation IDs:")
55
+ for path, methods in spec.raw_spec["paths"].items():
56
+ for method, details in methods.items():
57
+ print(f"- {details.get('operationId')}")
58
+
59
+ # Create LangChain tool from a specific endpoint
60
+ customer_tool = OpenAPITool.from_openapi_spec(
61
+ spec=spec,
62
+ operation_id="getCustomer", # use actual operationId from your spec
63
+ verbose=True
64
+ )
65
+
66
+ # Run a test call
67
+ result = customer_tool.run({
68
+ "filter[Country]": "USA",
69
+ "page[limit]": 3
70
+ })
71
+ print(result)
@@ -0,0 +1,19 @@
1
+ You are an AI planner that builds tool_context blocks for a JSON:API server using OpenAPI 3.0.
2
+
3
+ Use this OpenAPI file: [insert contents of nw_swagger_3.yaml]
4
+
5
+ Given the goal: "List all customers from Germany, limited to 2 results", generate a JSON MCP tool_context like this:
6
+
7
+ {
8
+ "tool": "json-api",
9
+ "method": "GET",
10
+ "url": "https://your-api-domain.com/Customer",
11
+ "query_params": {
12
+ "filter[Country]": "Germany",
13
+ "page[limit]": 2
14
+ },
15
+ "headers": {
16
+ "Accept": "application/vnd.api+json"
17
+ },
18
+ "expected_output": "List of customer records"
19
+ }
@@ -0,0 +1,13 @@
1
+ Model Context Protocol is a way for:
2
+
3
+ 1. **Bus User ad hoc flows** using existing published mcp services (vs. hard-coding in IT as an endpoint; flows can be cached for repeated use)
4
+
5
+ * ***Natural Language access*** to corporate databases for improved user interfaces
6
+
7
+ * LLMs ***choreograph*** multiple MCP calls (to 1 or more MCP servers) in a chain of calls - an agentic workflow. MCPs support shared contexts and goals, enabling the LLM to use the result from 1 call to determine whether the goals has been reached, or which service is appropriate to call next
8
+
9
+ 3. Chat agents to ***discover*** and ***call*** external servers, be they databases, APIs, file systems, etc. MCPs support shared contexts and goals, enabling the LLM
10
+
11
+ * ***Corporate database participation*** in such flows, by making key functions available as MCP calls.
12
+
13
+ This example is [explained here](https://apilogicserver.github.io/Docs/Integration-MCP/).
@@ -0,0 +1,295 @@
1
+ """
2
+ This simulates the MCP Client Executor,
3
+ which takes a natural language query and converts it into a tool context block:
4
+
5
+ 1. Discovers MCP servers (from config)
6
+ 2. Queries OpenAI's GPT-4 model to obtain the tool context based on a provided schema and a natural language query
7
+ 3. Processes the tool context (calls the indicated MCP (als) endpoints)
8
+
9
+ Notes:
10
+ * See: integration/mcp/README_mcp.md
11
+ * python api_logic_server_run.py
12
+
13
+ ToDo - email example is incomplete:
14
+ 1. Add email event handler (ala nw_sample/logic/declare_logic.py#send_n8n_message())
15
+ 2. And, respect the customer email_opt_out
16
+ 3. Needs to use date range
17
+ 4. Data incomplete
18
+
19
+ """
20
+
21
+ import json
22
+ import os
23
+ import openai
24
+ import requests
25
+
26
+ # Set your OpenAI API key
27
+ openai.api_key = os.getenv("APILOGICSERVER_CHATGPT_APIKEY")
28
+
29
+ server_url = os.getenv("APILOGICSERVER_URL", "http://localhost:5656/api")
30
+
31
+ # debug settings
32
+ test_type = 'orchestration' # 'simple_get' or 'orchestration'
33
+ create_tool_context_from_llm = True
34
+ ''' set to False to bypass LLM call and save 2-3 secs in testing '''
35
+ use_test_schema = False
36
+ ''' True means bypass discovery, use hard-coded schedma file '''
37
+
38
+ def discover_mcp_servers():
39
+ """ Discover the MCP servers by calling the /api/.well-known/mcp.json endpoint.
40
+ This function retrieves the list of available MCP servers and their capabilities.
41
+ """
42
+ global server_url, use_test_schema
43
+
44
+ # create schema_text (for prompt), by reading integration/mcp/mcp_schema.txt
45
+ if use_test_schema:
46
+ schema_file_path = os.path.join(os.path.dirname(__file__), "mcp_schema.txt")
47
+ try:
48
+ with open(schema_file_path, "r") as schema_file:
49
+ schema_text = schema_file.read()
50
+ except FileNotFoundError:
51
+ print(f"Schema file not found at {schema_file_path}.")
52
+ exit(1)
53
+ finally:
54
+ print(f"Schema file loaded from {schema_file_path}.")
55
+ return schema_text
56
+
57
+ # find the servers - read the mcp_server_discovery.json file
58
+ discovery_file_path = os.path.join(os.path.dirname(__file__), "mcp_server_discovery.json")
59
+ try:
60
+ with open(discovery_file_path, "r") as discovery_file:
61
+ discovery_data = json.load(discovery_file)
62
+ print(f"\nDiscovered MCP servers from config file: {discovery_file_path}:" + json.dumps(discovery_data, indent=4))
63
+ except FileNotFoundError:
64
+ print(f"Discovery file not found at {discovery_file_path}.")
65
+ except json.JSONDecodeError as e:
66
+ print(f"Error decoding JSON from {discovery_file_path}: {e}")
67
+
68
+ for each_server in discovery_data["servers"]:
69
+ discovery_url = each_server["schema_url"]
70
+
71
+ # Call the discovery_url to get the MCP/API schema
72
+ try:
73
+ response = requests.get(discovery_url)
74
+ if response.status_code == 200:
75
+ api_schema = response.json()
76
+ print()
77
+ print(f"\n1. API Schema from discovery schema_url: {discovery_url}:\n" +json.dumps(api_schema, indent=4))
78
+ else:
79
+ print(f"Failed to retrieve API schema from {discovery_url}: {response.status_code}")
80
+ except requests.RequestException as e:
81
+ print(f"Error calling OpenAPI URL: {e}")
82
+ return json.dumps(api_schema)
83
+
84
+
85
+ def get_user_nl_query():
86
+ """ Get the natural language query from the user. """
87
+
88
+ global test_type
89
+ # this doesn't work -- missing commands for mcp_server_executor....
90
+ default_request = "List the orders created more than 30 days ago, and post an email message to the order's customer offering a discount"
91
+
92
+ default_request = "List the orders created more than 30 days ago, and send a discount email to the customer for each one."
93
+ # date range? curl -X GET "http://localhost:5656/api/Order?filter=[{\’name\'}: {\’CreatedOn\’}, {\’op\’}: {\’gt\’}, {\’val\’}: {\’2022-05-14\’}]”
94
+
95
+ default_request = "List the orders for customer 5, and send a discount email to the customer for each one."
96
+
97
+ if test_type != 'orchestration':
98
+ default_request = "List customers with credit over 1000"
99
+
100
+ query = sys.argv[1] if len(sys.argv) > 1 else default_request
101
+
102
+ query += """
103
+ Respond with a JSON array of tool context blocks using:
104
+ - tool: 'json-api'
105
+ - JSON:API-compliant filtering (e.g., filter[CreatedOn][lt])
106
+ - Use {{ order.customer_id }} as a placeholder in the second step.
107
+ - Include method, url, query_params or body, headers, expected_output.
108
+ """
109
+ return query
110
+
111
+
112
+ def query_llm_with_nl(nl_query):
113
+ """
114
+ Query the LLM with a natural language query and schema text to generate a tool context block.
115
+
116
+ It handles both orchestration and simple GET requests.
117
+ """
118
+
119
+ global test_type, create_tool_context_from_llm
120
+
121
+ messages = [
122
+ {
123
+ "role": "system",
124
+ "content": "You are an API planner that converts natural language queries into MCP Tool Context blocks using JSON:API. Return only the tool context as JSON."
125
+ },
126
+ {
127
+ "role": "user",
128
+ "content": f"Schema:\n{schema_text}\n\nNatural language query: '{nl_query}'"
129
+ }
130
+ ]
131
+
132
+ # setup default tool_context to bypass LLM call and save 2-3 secs in testing
133
+ if test_type == 'orchestration': # orchestration: emails to pending orders
134
+ tool_context = \
135
+ [
136
+ {
137
+ "tool": "json-api",
138
+ "method": "GET",
139
+ "url": "http://localhost:5656/api/Order",
140
+ "query_params": {
141
+ "filter[customer_id]": 5
142
+ },
143
+ "headers": {
144
+ "Content-Type": "application/vnd.api+json"
145
+ },
146
+ "expected_output": "JSON array of orders for customer 5"
147
+ },
148
+ {
149
+ "tool": "email",
150
+ "method": "POST",
151
+ "url": "http://localhost:5656/api/Email",
152
+ "body": {
153
+ "to": "{{ order.customer_id }}",
154
+ "subject": "Discount Offer",
155
+ "message": "Dear Customer, We are offering a discount on your recent orders. Please check your account for more details."
156
+ },
157
+ "headers": {
158
+ "Content-Type": "application/json"
159
+ },
160
+ "expected_output": "Email sent confirmation"
161
+ }
162
+ ]
163
+ else: # simple get request - list customers with credit over 4000
164
+ tool_context = \
165
+ [
166
+ {
167
+ "tool": "json-api",
168
+ "method": "GET",
169
+ "url": "http://localhost:5656/api/Customer",
170
+ "query_params": {
171
+ "filter[credit_limit][gt]": 1000
172
+ },
173
+ "headers": {
174
+ "Content-Type": "application/vnd.api+json"
175
+ },
176
+ "expected_output": "JSON array of customers with credit limit over 1000"
177
+ }
178
+ ]
179
+
180
+ # Call the OpenAI API to generate the tool context
181
+ if create_tool_context_from_llm: # saves 2-3 seconds...
182
+ response = openai.chat.completions.create(
183
+ model="gpt-4",
184
+ messages=messages,
185
+ temperature=0.2
186
+ )
187
+
188
+ tool_context_str = response.choices[0].message.content
189
+ try:
190
+ tool_context = json.loads(tool_context_str)
191
+ except json.JSONDecodeError:
192
+ print("Failed to decode JSON from response:", tool_context_str)
193
+ return None
194
+
195
+ print("\n2. generated tool context from LLM:\n", json.dumps(tool_context, indent=4))
196
+ return tool_context
197
+
198
+
199
+ def process_tool_context(tool_context):
200
+ """ Process the orchestration request by executing multiple tool context blocks.
201
+ This function simulates the MCP Client Executor by executing the tool context blocks
202
+ against a live JSON:API server. It handles both GET and POST requests, and it can
203
+ orchestrate multiple requests based on the provided tool context.
204
+
205
+ Note the orchestration is processed by the client executor (here), not the server executor.
206
+
207
+ Research:
208
+
209
+ 1. How is this a "USB", since the request was specific about JSON:API?
210
+ 2. How is it clear to loop through the tool_context[0] and call tool_context[1]?
211
+ """
212
+ global server_url
213
+
214
+ def get_query_param_filter(query_params):
215
+ """ return json:api filter
216
+
217
+ query_params might be:
218
+ "query_params": {
219
+ "filter[credit_limit][gt]": 1000 }
220
+ or:
221
+ "query_params": {
222
+ "filter[customer_id]": 5},
223
+
224
+ """
225
+ query_param_filter = ''
226
+ if isinstance(query_params, dict):
227
+ for each_key, each_value in query_params.items():
228
+ if isinstance(each_value, dict):
229
+ for sub_key, sub_value in each_value.items():
230
+ query_param_filter += f"&{each_key}[{sub_key}]={sub_value}"
231
+ else:
232
+ query_param_filter += f"&{each_key}={each_value}"
233
+ # query_params = ''
234
+ elif isinstance(query_params, dict):
235
+ assert False, "Query Params dict tbd"
236
+ return query_param_filter
237
+
238
+
239
+ if isinstance(tool_context, dict):
240
+ query_params = tool_context["query_params"]
241
+ query_param_filter = get_query_param_filter(query_params)
242
+ mcp_response = requests.get(
243
+ tool_context["url"],
244
+ headers=tool_context["headers"],
245
+ params=query_param_filter
246
+ )
247
+ elif isinstance(tool_context, list):
248
+ context_data = {}
249
+ added_rows = 0
250
+
251
+ for each_block in tool_context:
252
+ if each_block["tool"] in ["json-api", "email"]:
253
+ if each_block["method"] == "GET":
254
+ query_param_filter = get_query_param_filter(each_block["query_params"])
255
+ mcp_response = requests.get(
256
+ url = each_block["url"],
257
+ headers=each_block["headers"],
258
+ params=query_param_filter
259
+ )
260
+ context_data = mcp_response.json()['data'] # result rows...
261
+ elif each_block["method"] in ["POST"]:
262
+ add_rows = 0
263
+ for each_order in context_data:
264
+ url = each_block["url"]
265
+ json_update_data = { 'data': {"type": "Email", 'attributes': {} } }
266
+ json_update_data_attributes = json_update_data["data"]["attributes"]
267
+ json_update_data_attributes["customer_id"] = context_data[0]['attributes']["customer_id"]
268
+ json_update_data_attributes["message"] = each_block["body"]["message"]
269
+ # eg: POST http://localhost:5656/api/Email {'data': {'type': 'Email', 'attributes': {'customer_id': 5, 'message': {'to': '{{ order.customer_id }}', 'subject': 'Discount for your order', 'body': 'Dear customer, you have a discount for your recent order. Thank you for shopping with us.'}}}}
270
+ mcp_response = requests.post(
271
+ url=url,
272
+ headers=each_block["headers"],
273
+ json=json_update_data
274
+ )
275
+ add_rows += 1
276
+ pass
277
+ else:
278
+ print("Invalid tool context format. Expected a dictionary or a list.")
279
+ return None
280
+ print("\n3. MCP Server (als) Response:\n", mcp_response.text)
281
+ return mcp_response
282
+
283
+
284
+ if __name__ == "__main__":
285
+ import sys
286
+
287
+ schema_text = discover_mcp_servers() # see: 1-discovery-from-als
288
+
289
+ query = get_user_nl_query()
290
+
291
+ tool_context = query_llm_with_nl(query) # see: 2-tool-context-from-LLM
292
+
293
+ mcp_response = process_tool_context(tool_context) # see: 3-MCP-server response
294
+
295
+ print("\nTest complete.\n")
@@ -0,0 +1,47 @@
1
+ Customer:
2
+ - id (INTEGER)
3
+ - name (VARCHAR)
4
+ - balance (DECIMAL)
5
+ - credit_limit (DECIMAL)
6
+ * Filterable: name, balance, credit_limit
7
+ * Example: 'List customers with credit over 5000'
8
+
9
+ Item:
10
+ - id (INTEGER)
11
+ - order_id (INTEGER)
12
+ - product_id (INTEGER)
13
+ - quantity (INTEGER)
14
+ - amount (DECIMAL)
15
+ - unit_price (DECIMAL)
16
+ * Filterable: order_id, product_id, unit_price
17
+ * Relationships:
18
+ ↪ relates to Order
19
+ ↪ relates to Product
20
+ * Example: 'Find all items for order 42'
21
+
22
+ Email:
23
+ - id (INTEGER)
24
+ - message (VARCHAR)
25
+ - customer_id (INTEGER)
26
+ * Relationships:
27
+ ↪ relates to Customer
28
+ * Example: 'Post an message "text" for customer 2 means POST to this resource'
29
+
30
+ Order:
31
+ - id (INTEGER)
32
+ - notes (VARCHAR)
33
+ - customer_id (INTEGER)
34
+ - CreatedOn (DATE)
35
+ - date_shipped (DATE)
36
+ - amount_total (DECIMAL)
37
+ * Filterable: notes, customer_id, date_shipped, amount_total
38
+ * Relationships:
39
+ ↪ relates to Customer
40
+ * Example: 'Get orders shipped in 2024'
41
+
42
+ Product:
43
+ - id (INTEGER)
44
+ - name (VARCHAR)
45
+ - unit_price (DECIMAL)
46
+ * Filterable: name, unit_price
47
+ * Example: 'Show products with price over 100'
@@ -0,0 +1,9 @@
1
+ {
2
+ "servers": [
3
+ {
4
+ "name": "API Logic Server: basic_demo",
5
+ "base_url": "http://localhost:5656",
6
+ "schema_url": "http://localhost:5656/.well-known/mcp.json"
7
+ }
8
+ ]
9
+ }
@@ -0,0 +1,49 @@
1
+
2
+ description: Flag high-value customers with recent activity and send to marketing service
3
+
4
+ steps:
5
+ - id: fetch_2024_orders
6
+ server: https://erp.company.com
7
+ tool_context:
8
+ tool: json-api
9
+ method: GET
10
+ url: /Order
11
+ query_params:
12
+ filter[date_shipped][gte]: "2024-01-01"
13
+ filter[date_shipped][lte]: "2024-12-31"
14
+ headers:
15
+ Accept: application/vnd.api+json
16
+ expected_output: Orders shipped in 2024
17
+
18
+ - id: aggregate_by_customer
19
+ type: local_processing
20
+ input: fetch_2024_orders
21
+ operation: |
22
+ Group orders by customer_id and sum amount_total.
23
+ Return list of customer_ids with total > 10000.
24
+
25
+ - id: fetch_customer_details
26
+ server: https://crm.company.com
27
+ tool_context:
28
+ tool: json-api
29
+ method: GET
30
+ url: /Customer
31
+ query_params:
32
+ filter[id]: "{{ aggregate_by_customer.output.customer_ids }}"
33
+ headers:
34
+ Accept: application/vnd.api+json
35
+ expected_output: High-value customer details
36
+
37
+ - id: notify_marketing
38
+ server: https://marketing.company.com
39
+ tool_context:
40
+ tool: json-api
41
+ method: POST
42
+ url: /CampaignTrigger
43
+ body:
44
+ campaign: "VIP Outreach Q1"
45
+ customer_ids: "{{ aggregate_by_customer.output.customer_ids }}"
46
+ headers:
47
+ Accept: application/vnd.api+json
48
+ Content-Type: application/json
49
+ expected_output: Marketing campaign initiated
@@ -0,0 +1,73 @@
1
+ import openai
2
+ import requests
3
+ import json
4
+ import os
5
+
6
+ # Set your OpenAI API key here or via environment variable
7
+ openai.api_key = os.getenv("OPENAI_API_KEY") or "sk-proj-..."
8
+
9
+ # The base ngrok URL of your live JSON:API server
10
+ NGROK_BASE_URL = "https://mcp_url.ngrok-free.app"
11
+
12
+ def ask_gpt_for_tool_context(natural_language_goal):
13
+ prompt = f"""
14
+ You are an API planning assistant.
15
+
16
+ You receive a natural language goal and must generate a JSON tool_context for an API call.
17
+
18
+ The API follows JSON:API 1.1 and lives at: {NGROK_BASE_URL}/api
19
+ Available resources: Customer, Order, Product
20
+
21
+ Goal: "{natural_language_goal}"
22
+
23
+ Respond ONLY with JSON in the following format:
24
+ {{
25
+ "method": "GET",
26
+ "url": "<full URL>",
27
+ "query_params": {{ ... }},
28
+ "headers": {{
29
+ "Accept": "application/vnd.api+json"
30
+ }},
31
+ "expected_output": "Brief description of expected result"
32
+ }}
33
+ """
34
+
35
+ response = openai.ChatCompletion.create(
36
+ model="gpt-4",
37
+ messages=[{"role": "user", "content": prompt}],
38
+ temperature=0
39
+ )
40
+
41
+ # Extract tool_context JSON block from the assistant message
42
+ message = response["choices"][0]["message"]["content"]
43
+ try:
44
+ tool_context = json.loads(message)
45
+ return tool_context
46
+ except json.JSONDecodeError:
47
+ print("Failed to parse GPT response as JSON:")
48
+ print(message)
49
+ return None
50
+
51
+ def execute_tool_context(tool_context):
52
+ try:
53
+ response = requests.get(
54
+ tool_context["url"],
55
+ headers=tool_context["headers"],
56
+ params=tool_context["query_params"],
57
+ timeout=10
58
+ )
59
+ print(f"✅ Response ({response.status_code}):")
60
+ print(json.dumps(response.json(), indent=2))
61
+ except Exception as e:
62
+ print(f"❌ Request failed: {e}")
63
+
64
+ if __name__ == "__main__":
65
+ print("Enter your natural language goal (e.g., 'List 3 customers from Germany'):")
66
+ goal = input("> ")
67
+
68
+ tc = ask_gpt_for_tool_context(goal)
69
+ if tc:
70
+ print("GPT tool_context:")
71
+ print(json.dumps(tc, indent=2))
72
+ print("Executing API call...")
73
+ execute_tool_context(tc)
@@ -0,0 +1,5 @@
1
+ curl -X 'GET' \
2
+ 'http://localhost:5656/api/Customer/?filter%5BCountry%5D=Germany' \
3
+ -H 'accept: application/vnd.api+json' \
4
+ -H 'Content-Type: application/vnd.api+json'
5
+
@@ -0,0 +1,10 @@
1
+ [
2
+ {
3
+ "role": "system",
4
+ "content": "You are a backend planner that translates natural language queries into JSON:API tool context blocks. You are given a resource schema and a user query. Respond only with a JSON block that specifies how to call the JSON:API using filter parameters."
5
+ },
6
+ {
7
+ "role": "user",
8
+ "content": "Schema:\n\nCustomer:\n - id (INTEGER)\n - name (VARCHAR)\n - balance (DECIMAL)\n - credit_limit (DECIMAL)\n * Filterable: name, balance, credit_limit\n * Example: 'List customers with credit over 5000'\n\nItem:\n - id (INTEGER)\n - order_id (INTEGER)\n - product_id (INTEGER)\n - quantity (INTEGER)\n - amount (DECIMAL)\n - unit_price (DECIMAL)\n * Filterable: order_id, product_id, unit_price\n * Relationships:\n ↪ relates to Order\n ↪ relates to Product\n * Example: 'Find all items for order 42'\n\nOrder:\n - id (INTEGER)\n - notes (VARCHAR)\n - customer_id (INTEGER)\n - date_shipped (DATE)\n - amount_total (DECIMAL)\n * Filterable: notes, customer_id, date_shipped, amount_total\n * Relationships:\n ↪ relates to Customer\n * Example: 'Get orders shipped in 2024'\n\nProduct:\n - id (INTEGER)\n - name (VARCHAR)\n - unit_price (DECIMAL)\n * Filterable: name, unit_price\n * Example: 'Show products with price over 100'\n\n---\n\nNatural language query: 'List all customers with credit limits over 4000.'\n\nRespond with a JSON object like:\n{\n \"tool\": \"json-api\",\n \"method\": \"GET\",\n \"url\": \"https://your-api-server/Customer\",\n \"query_params\": { \"filter[credit_limit][gt]\": \"4000\" },\n \"headers\": { \"Accept\": \"application/vnd.api+json\" },\n \"expected_output\": \"List of customers with high credit limits\"\n}"
9
+ }
10
+ ]
@@ -0,0 +1,12 @@
1
+ {
2
+ "tool": "json-api",
3
+ "method": "GET",
4
+ "url": "https://your-api-server/Customer",
5
+ "query_params": {
6
+ "filter[credit_limit][gt]": "4000"
7
+ },
8
+ "headers": {
9
+ "Accept": "application/vnd.api+json"
10
+ },
11
+ "expected_output": "List of customers with high credit limits"
12
+ }
@@ -0,0 +1,38 @@
1
+ Customer:
2
+ - id (INTEGER)
3
+ - name (VARCHAR)
4
+ - balance (DECIMAL)
5
+ - credit_limit (DECIMAL)
6
+ * Filterable: name, balance, credit_limit
7
+ * Example: 'List customers with credit over 5000'
8
+
9
+ Item:
10
+ - id (INTEGER)
11
+ - order_id (INTEGER)
12
+ - product_id (INTEGER)
13
+ - quantity (INTEGER)
14
+ - amount (DECIMAL)
15
+ - unit_price (DECIMAL)
16
+ * Filterable: order_id, product_id, unit_price
17
+ * Relationships:
18
+ ↪ relates to Order
19
+ ↪ relates to Product
20
+ * Example: 'Find all items for order 42'
21
+
22
+ Order:
23
+ - id (INTEGER)
24
+ - notes (VARCHAR)
25
+ - customer_id (INTEGER)
26
+ - date_shipped (DATE)
27
+ - amount_total (DECIMAL)
28
+ * Filterable: notes, customer_id, date_shipped, amount_total
29
+ * Relationships:
30
+ ↪ relates to Customer
31
+ * Example: 'Get orders shipped in 2024'
32
+
33
+ Product:
34
+ - id (INTEGER)
35
+ - name (VARCHAR)
36
+ - unit_price (DECIMAL)
37
+ * Filterable: name, unit_price
38
+ * Example: 'Show products with price over 100'