ApiLogicServer 14.5.4__py3-none-any.whl → 14.5.14__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 (56) hide show
  1. api_logic_server_cli/api_logic_server.py +4 -122
  2. api_logic_server_cli/api_logic_server_info.yaml +3 -3
  3. api_logic_server_cli/create_from_model/__pycache__/api_logic_server_utils.cpython-312.pyc +0 -0
  4. api_logic_server_cli/create_from_model/__pycache__/dbml.cpython-312.pyc +0 -0
  5. api_logic_server_cli/create_from_model/api_logic_server_utils.py +29 -10
  6. api_logic_server_cli/create_from_model/dbml.py +16 -12
  7. api_logic_server_cli/database/basic_demo.sqlite +0 -0
  8. api_logic_server_cli/genai/genai.py +4 -0
  9. api_logic_server_cli/genai/genai_svcs.py +10 -2
  10. api_logic_server_cli/manager.py +3 -1
  11. api_logic_server_cli/prototypes/base/.vscode/launch.json +40 -0
  12. api_logic_server_cli/prototypes/base/api/api_discovery/mcp_discovery.py +58 -0
  13. api_logic_server_cli/prototypes/base/database/system/SAFRSBaseX.py +68 -2
  14. api_logic_server_cli/prototypes/base/database/system/SAFRSBaseX.pyZ +73 -0
  15. api_logic_server_cli/prototypes/{basic_demo/customizations/integration → base/integration/mcp}/.DS_Store +0 -0
  16. api_logic_server_cli/prototypes/base/integration/mcp/README_mcp.md +15 -0
  17. api_logic_server_cli/prototypes/{basic_demo/customizations → base}/integration/mcp/mcp_client_executor.py +91 -103
  18. api_logic_server_cli/prototypes/base/integration/mcp/mcp_schema.txt +47 -0
  19. api_logic_server_cli/prototypes/base/integration/mcp/mcp_server_discovery.json +9 -0
  20. api_logic_server_cli/prototypes/base/integration/mcp/test_notes.txt +37 -0
  21. api_logic_server_cli/prototypes/basic_demo/README.md +251 -91
  22. api_logic_server_cli/prototypes/basic_demo/customizations/api/api_discovery/mcp_discovery.py +1 -1
  23. api_logic_server_cli/prototypes/basic_demo/customizations/database/db.sqlite +0 -0
  24. api_logic_server_cli/prototypes/basic_demo/customizations/database/models.py +27 -12
  25. api_logic_server_cli/prototypes/basic_demo/customizations/database/system/SAFRSBaseX.py +1 -1
  26. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/Zmcp_client_executor.py +294 -0
  27. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/mcp_tool_context.json +25 -0
  28. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/test_notes.txt +37 -0
  29. api_logic_server_cli/prototypes/basic_demo/customizations/logic/declare_logic.py +1 -20
  30. api_logic_server_cli/prototypes/basic_demo/customizations/logic/logic_discovery/email_request.py +47 -0
  31. api_logic_server_cli/prototypes/basic_demo/customizations/logic/logic_discovery/mcp_client_executor_request.py +320 -0
  32. api_logic_server_cli/prototypes/basic_demo/customizations/logic/logic_discovery/simple_constraints.py +25 -0
  33. api_logic_server_cli/prototypes/basic_demo/customizations/ui/admin/admin.yaml +39 -32
  34. api_logic_server_cli/prototypes/basic_demo/iteration/database/db.sqlite +0 -0
  35. api_logic_server_cli/prototypes/basic_demo/iteration/ui/admin/admin.yaml +39 -44
  36. api_logic_server_cli/prototypes/manager/.vscode/launch.json +21 -0
  37. api_logic_server_cli/prototypes/manager/{README.md → READMEz.md} +4 -4
  38. api_logic_server_cli/prototypes/manager/REAMDE.md +1057 -0
  39. api_logic_server_cli/prototypes/manager/system/genai/mcp_learning/mcp.prompt +27 -0
  40. api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/install-ApiLogicServer-dev.sh +2 -1
  41. {apilogicserver-14.5.4.dist-info → apilogicserver-14.5.14.dist-info}/METADATA +1 -1
  42. {apilogicserver-14.5.4.dist-info → apilogicserver-14.5.14.dist-info}/RECORD +46 -42
  43. {apilogicserver-14.5.4.dist-info → apilogicserver-14.5.14.dist-info}/WHEEL +1 -1
  44. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/3_executor_test_agent.py +0 -52
  45. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/README_functon.md +0 -201
  46. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/ai_plugin.json +0 -17
  47. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/nw-swagger_3.json +0 -1731
  48. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/snippets.txt +0 -5
  49. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3 genai_demo_with_get.json +0 -1731
  50. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3.json +0 -1782
  51. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3_genai_demo.json +0 -264
  52. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3_genai_demo_with_update.json +0 -1782
  53. api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/auto_discovery.py +0 -52
  54. {apilogicserver-14.5.4.dist-info → apilogicserver-14.5.14.dist-info}/entry_points.txt +0 -0
  55. {apilogicserver-14.5.4.dist-info → apilogicserver-14.5.14.dist-info}/licenses/LICENSE +0 -0
  56. {apilogicserver-14.5.4.dist-info → apilogicserver-14.5.14.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,73 @@
1
+ from sqlalchemy.ext.declarative import declarative_base
2
+ from sqlalchemy import Column, DECIMAL, Date, ForeignKey, Integer, String
3
+ from safrs import SAFRSBase
4
+ from flask_login import UserMixin
5
+ import safrs, flask_sqlalchemy
6
+ from safrs import jsonapi_attr
7
+ from flask_sqlalchemy import SQLAlchemy
8
+ from datetime import datetime
9
+
10
+ Base = declarative_base() # type: flask_sqlalchemy.model.DefaultMeta
11
+ #vh new x
12
+ @classmethod
13
+ def jsonapi_filter(cls):
14
+ """
15
+ Use this to override SAFRS JSON:API filtering
16
+
17
+ Returns:
18
+ _type_: SQLAlchemy query filter
19
+ """
20
+ from sqlalchemy import text, or_, and_
21
+ from flask import request
22
+ expressions = []
23
+ sqlWhere = ""
24
+ query = cls._s_query
25
+ if args := request.args:
26
+ from api.system.expression_parser import advancedFilter
27
+ expressions, sqlWhere = advancedFilter(cls, args)
28
+ if sqlWhere != "":
29
+ return query.filter(text(sqlWhere))
30
+ else:
31
+ return query.filter(or_(*expressions))
32
+
33
+ class SAFRSBaseX(SAFRSBase, safrs.DB.Model):
34
+ __abstract__ = True
35
+ if do_enable_ont_advanced_filters := False:
36
+ jsonapi_filter = jsonapi_filter
37
+
38
+ def _s_parse_attr_value(self, attr_name: str, attr_val: any):
39
+ """
40
+ Parse the given jsonapi attribute value so it can be stored in the db
41
+ :param attr_name: attribute name
42
+ :param attr_val: attribute value
43
+ :return: parsed value
44
+ """
45
+ attr = self.__class__._s_jsonapi_attrs.get(attr_name, None)
46
+ if hasattr(attr, "type"): # pragma: no cover
47
+
48
+ if str(attr.type) in ["DATE", "DATETIME"] and attr_val:
49
+ try:
50
+ attr_val = attr_val.replace("T", " ")
51
+ datetime.strptime(attr_val, '%Y-%m-%d %H:%M')
52
+ attr_val += ":00"
53
+ except ValueError:
54
+ pass
55
+ except Exception as exc:
56
+ safrs.log.warning(exc)
57
+
58
+
59
+ return super()._s_parse_attr_value(attr_name, attr_val)
60
+
61
+ class TestBase(Base):
62
+ __abstract__ = True
63
+ def __init__(self, *args, **kwargs):
64
+ for name, val in kwargs.items():
65
+ col = getattr(self.__class__, name)
66
+ if 'amount_total' == name:
67
+ debug_stop = 'stop'
68
+ if val is not None:
69
+ if str(col.type) in ["DATE", "DATETIME"]:
70
+ pass
71
+ else:
72
+ kwargs[name] = col.type.python_type(val)
73
+ return super().__init__(*args, **kwargs)
@@ -0,0 +1,15 @@
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/).
14
+
15
+ > Note: this sample uses multi-term filters. These are usually OR'd together, but this example requires AND. This is provided by `database/system/SAFRSBaseX.py` (see `return query.filter(operator.and_(*expressions)`) in `_s_filter()`), activated in `config/server_setup.py`.
@@ -13,10 +13,12 @@ Notes:
13
13
  """
14
14
 
15
15
  import json
16
- import os
17
- import re
16
+ import os, sys
17
+ from typing import Dict, List
18
18
  import openai
19
19
  import requests
20
+ from logic_bank.logic_bank import Rule
21
+ from logic_bank.util import ConstraintException
20
22
 
21
23
  # Set your OpenAI API key
22
24
  openai.api_key = os.getenv("APILOGICSERVER_CHATGPT_APIKEY")
@@ -37,24 +39,13 @@ def discover_mcp_servers():
37
39
  global server_url, use_test_schema
38
40
 
39
41
  # create schema_text (for prompt), by reading integration/mcp/mcp_schema.txt
40
- if use_test_schema:
41
- schema_file_path = os.path.join(os.path.dirname(__file__), "mcp_schema.txt")
42
- try:
43
- with open(schema_file_path, "r") as schema_file:
44
- schema_text = schema_file.read()
45
- except FileNotFoundError:
46
- print(f"Schema file not found at {schema_file_path}.")
47
- exit(1)
48
- finally:
49
- print(f"Schema file loaded from {schema_file_path}.")
50
- return schema_text
51
42
 
52
43
  # find the servers - read the mcp_server_discovery.json file
53
- discovery_file_path = os.path.join(os.path.dirname(__file__), "mcp_server_discovery.json")
44
+ discovery_file_path = os.path.join(os.path.dirname(__file__), "../../integration/mcp/mcp_server_discovery.json")
54
45
  try:
55
46
  with open(discovery_file_path, "r") as discovery_file:
56
47
  discovery_data = json.load(discovery_file)
57
- print(f"\nDiscovered MCP servers from config file: {discovery_file_path}:" + json.dumps(discovery_data, indent=4))
48
+ print(f"\n1. Discovered MCP servers from config file: {discovery_file_path}:" + json.dumps(discovery_data, indent=4))
58
49
  except FileNotFoundError:
59
50
  print(f"Discovery file not found at {discovery_file_path}.")
60
51
  except json.JSONDecodeError as e:
@@ -69,7 +60,8 @@ def discover_mcp_servers():
69
60
  if response.status_code == 200:
70
61
  api_schema = response.json()
71
62
  print()
72
- print(f"\n1. API Schema from discovery schema_url: {discovery_url}:\n" +json.dumps(api_schema, indent=4))
63
+ request_print = json.dumps(api_schema, indent=4)[0:400] + '\n... etc' # limit for readability
64
+ print(f"\n\nAPI Schema from discovery schema_url: {discovery_url}:\n" + request_print)
73
65
  else:
74
66
  print(f"Failed to retrieve API schema from {discovery_url}: {response.status_code}")
75
67
  except requests.RequestException as e:
@@ -77,24 +69,33 @@ def discover_mcp_servers():
77
69
  return json.dumps(api_schema)
78
70
 
79
71
 
80
- def get_user_nl_query():
72
+ def get_user_nl_query_and_training(query: str):
81
73
  """ Get the natural language query from the user.
82
- Add instructions for the LLM to generate a tool context block.
74
+ Add training for the LLM to generate a tool context block.
83
75
 
84
76
  """
85
77
 
86
78
  global test_type
87
-
88
- if len(sys.argv) > 1 and sys.argv[1] != 'go':
89
- request = sys.argv[1]
79
+ # read file docs/mcp_learning/mcp.prompt
80
+ prompt_file_path = os.path.join(os.path.dirname(__file__), "../../docs/mcp_learning/mcp.prompt")
81
+ if os.path.exists(prompt_file_path):
82
+ with open(prompt_file_path, "r") as prompt_file:
83
+ training_prompt = prompt_file.read()
84
+ # print(f"\nLoaded training prompt from {prompt_file_path}:\n{training_prompt}")
90
85
  else:
91
- request = "List the unshipped orders created before 2023-07-14, and send a discount email to the customer for each one."
92
- if test_type != 'orchestration':
93
- request = "List customers with credit over 1000"
94
- return request
86
+ training_prompt = ""
87
+ print(f"Prompt file not found at {prompt_file_path}.")
95
88
 
89
+ # if 1 argument, use it as the query
90
+ query_actual = query
91
+ if len(sys.argv) > 1:
92
+ query_actual = sys.argv[1]
93
+ if query_actual == '':
94
+ query_actual = "list customers with balance over 100."
95
+ return query_actual + ";\n\n" + training_prompt
96
96
 
97
- def query_llm_with_nl(nl_query):
97
+
98
+ def query_llm_with_nl(schema_text, nl_query):
98
99
  """
99
100
  Query the LLM with a natural language query and schema text to generate a tool context block.
100
101
 
@@ -103,6 +104,7 @@ def query_llm_with_nl(nl_query):
103
104
 
104
105
  global test_type, create_tool_context_from_llm
105
106
 
107
+ content = f"Natural language query:\n {nl_query}\nSchema:\n{schema_text}"
106
108
  messages = [
107
109
  {
108
110
  "role": "system",
@@ -110,70 +112,18 @@ def query_llm_with_nl(nl_query):
110
112
  },
111
113
  {
112
114
  "role": "user",
113
- "content": f"Schema:\n{schema_text}\n\nNatural language query: '{nl_query}'"
115
+ "content": f"{content}"
114
116
  }
115
117
  ]
116
118
 
117
- # setup default tool_context to bypass LLM call and save 2-3 secs in testing
118
- if test_type == 'orchestration': # orchestration: emails to pending orders
119
-
120
- tool_context = \
121
- {
122
- "tool_type": "json-api",
123
- "schema_version": "1.0",
124
- "base_url": "http://localhost:5656/api",
125
- "resources": [
126
- {
127
- "path": "/Order",
128
- "method": "GET",
129
- "query_params": [
130
- {
131
- "name": "date_shipped",
132
- "op": "eq",
133
- "val": None
134
- },
135
- {
136
- "name": "date_created",
137
- "op": "lt",
138
- "val": "2023-07-14"
139
- }
140
- ],
141
- "headers": [],
142
- "expected_output": []
143
- },
144
- {
145
- "path": "/Email",
146
- "method": "POST",
147
- "body": {
148
- "customer_id": "{{ order.customer_id }}",
149
- "message": "You have a discount on your unshipped order."
150
- },
151
- "headers": [],
152
- "expected_output": []
153
- }
154
- ]
155
- }
156
- else: # simple get request - list customers with credit over 1000
157
- tool_context = \
158
- {
159
- "tool_type": "json-api",
160
- "schema_version": "1.0",
161
- "base_url": "http://localhost:5656/api",
162
- "resources": [
163
- {
164
- "path": "/Customer",
165
- "method": "GET",
166
- "query_params": [
167
- {
168
- "name": "credit_limit",
169
- "op": "gt",
170
- "val": "1000"
171
- }
172
- ]
173
- }
174
- ]
175
- } # Call the OpenAI API to generate the tool context
176
- if create_tool_context_from_llm: # saves 2-3 seconds...
119
+ request_print = content[0:1200] + '\n... etc' # limit for readability
120
+ print("\n\n2a. LLM request:\n", request_print)
121
+ # print("\n2b. NL Query:\n", nl_query)
122
+ # print("\n2c. schema_text: (truncated) \n")
123
+ # schema_print = json.dumps(json.loads(schema_text), indent=4)[:400] # limit for readability
124
+ # print(schema_print)
125
+
126
+ if create_tool_context_from_llm: # takes 2-3 seconds...
177
127
  response = openai.chat.completions.create(
178
128
  model="gpt-4",
179
129
  messages=messages,
@@ -188,17 +138,17 @@ def query_llm_with_nl(nl_query):
188
138
  print("Failed to decode JSON from response:", tool_context_str)
189
139
  return None
190
140
 
191
- print("\n\n2a. LLM request:\n", json.dumps(messages, indent=4))
192
- print("\n2b. NL Query:\n", nl_query)
193
- print("\n2c. schema_text: \n", json.dumps(json.loads(schema_text), indent=4)) #schema_text[0:100])
194
141
  print("\n2d. generated tool context from LLM:\n", json.dumps(tool_context, indent=4))
142
+
143
+ if "resources" not in tool_context:
144
+ raise ConstraintException("GenAI Error - LLM response does not contain 'resources'.")
195
145
  return tool_context
196
146
 
197
147
 
198
148
  def process_tool_context(tool_context):
199
149
  """ Process the orchestration request by executing multiple tool context blocks.
200
- This function simulates the MCP Client Executor by executing the tool context blocks
201
- against a live JSON:API server. It handles both GET and POST requests, and it can
150
+ This executes the tool context blocks against a live JSON:API server.
151
+ It handles both GET and POST requests, and it can
202
152
  orchestrate multiple requests based on the provided tool context.
203
153
 
204
154
  Note the orchestration is processed by the client executor (here), not the server executor.
@@ -247,36 +197,72 @@ def process_tool_context(tool_context):
247
197
  query_param_filter = 'filter=' + str(query_params)
248
198
  # use urlencode to convert to JSON:API format...
249
199
  # val urllib.parse.quote() or urllib.parse.urlencode()
250
- # tool instructions... filtering, email etc
200
+ # tool instructions... filtering, email etc "null"
251
201
  query_param_filter = query_param_filter.replace("'", '"') # convert single quotes to double quotes
252
202
  query_param_filter = query_param_filter.replace("None", 'null')
253
- query_param_filter = query_param_filter.replace("date_created", 'CreatedOn') # TODO - why this name?
203
+ query_param_filter = query_param_filter.replace('"null"', 'null')
204
+ # query_param_filter = query_param_filter.replace("date_created", 'CreatedOn') # TODO - why this name?
254
205
  return query_param_filter # end get_query_param_filter
255
206
 
207
+ def move_fields(src: dict, dest: dict, context_data: dict):
208
+ """ Move fields from src to dest, replacing any variables with their values from context_data."""
209
+ for variable_name, value in src.items():
210
+ move_value = value
211
+ if move_value.startswith("{") and move_value.endswith("}"):
212
+ # strip the braces, and get the name after the first dot, # eg: "{Order.customer_id}" ==> "customer_id"``
213
+ move_name = move_value[1:-1] # strip the braces
214
+ if '.' in move_value:
215
+ move_name = move_name.split('.', 1)[1]
216
+ move_value = context_data['attributes'][move_name]
217
+ dest[variable_name] = move_value
218
+ return dest
219
+
220
+ def print_get_response(query_param_filter, mcp_response):
221
+ """ Print the response from the GET request. """
222
+ print("\n3. MCP Server (als) GET filter(query_param_filter):\n", query_param_filter)
223
+ print(" GET Response:\n", mcp_response.text)
224
+ results : List[Dict] = mcp_response.json()['data']
225
+ # print results in a table format
226
+ if results:
227
+ # Get all unique keys from all result dicts
228
+ keys = set()
229
+ for row in results:
230
+ if isinstance(row, dict):
231
+ keys.update(row.keys())
232
+ keys = list(keys)
233
+ # Print header
234
+ print("\n| " + " | ".join(keys) + " |")
235
+ print("|" + "|".join(["---"] * len(keys)) + "|")
236
+ # Print rows
237
+ for row in results:
238
+ print("| " + " | ".join(str(row.get(k, "")) for k in keys) + " |")
239
+ else:
240
+ print("No results found.")
241
+
256
242
  assert isinstance(tool_context, (dict, list)), "Tool context expected to be a dictionary"
257
243
  context_data = {}
258
244
  added_rows = 0
259
245
 
260
246
  for each_block in tool_context["resources"]:
261
- if True: # TODO - add check for "tool": "json-api"
247
+ if process_tool_context := True:
262
248
  if each_block["method"] == "GET":
263
249
  query_param_filter = get_query_param_filter(each_block["query_params"])
264
250
  headers = {"Content-Type": "application/vnd.api+json"}
265
251
  if "headers" in each_block:
266
252
  headers.update(each_block["headers"])
267
253
  mcp_response = requests.get(
268
- url = tool_context["base_url"] + each_block["path"],
254
+ url = each_block["base_url"] + each_block["path"],
269
255
  headers=headers,
270
256
  params=query_param_filter
271
257
  )
272
258
  context_data = mcp_response.json()['data'] # result rows...
259
+ print_get_response(query_param_filter, mcp_response)
273
260
  elif each_block["method"] in ["POST"]:
274
261
  for each_order in context_data:
275
- url = tool_context["base_url"] + each_block["path"]
262
+ url = each_block["base_url"] + each_block["path"]
276
263
  json_update_data = { 'data': {"type": "Email", 'attributes': {} } }
277
264
  json_update_data_attributes = json_update_data["data"]["attributes"]
278
- json_update_data_attributes["customer_id"] = context_data[0]['attributes']["customer_id"] # TODO - fix
279
- json_update_data_attributes["message"] = each_block["body"]["message"]
265
+ move_fields( src= each_block["body"], dest=json_update_data_attributes, context_data=each_order)
280
266
  # 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.'}}}}
281
267
  headers = {"Content-Type": "application/vnd.api+json"}
282
268
  if "headers" in each_block:
@@ -288,20 +274,22 @@ def process_tool_context(tool_context):
288
274
  )
289
275
  added_rows += 1
290
276
  pass
291
- print("\n3. MCP Server (als) Response:\n", mcp_response.text)
277
+ print("\n3. MCP Server (als) POST Response:\n", mcp_response.text)
292
278
  if added_rows > 0:
293
279
  print(f"...Added {added_rows} rows to the database; last row (only) shown above.")
294
280
  return mcp_response
295
281
 
296
282
 
297
283
  if __name__ == "__main__":
298
- import sys
284
+
285
+ # to run: Run Config > Run designated Python file
299
286
 
300
287
  schema_text = discover_mcp_servers() # see: 1-discovery-from-als
301
288
 
302
- query = get_user_nl_query()
289
+ query = "list customers with balance over 100"
290
+ prompt = get_user_nl_query_and_training(query) # set breakpoint here, view log, then step
303
291
 
304
- tool_context = query_llm_with_nl(query) # see: 2-tool-context-from-LLM
292
+ tool_context = query_llm_with_nl(schema_text, prompt) # see: 2-tool-context-from-LLM
305
293
 
306
294
  mcp_response = process_tool_context(tool_context) # see: 3-MCP-server response
307
295
 
@@ -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,37 @@
1
+ LLM request - new...?
2
+
3
+ [
4
+ {
5
+ "role": "system",
6
+ "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."
7
+ },
8
+ {
9
+ "role": "user",
10
+ "content": "Schema:\n{\"base_url\": \"http://localhost:5656/api\", \"description\": \"API Logic Project: basic_demo\", \"email_services\": \"iff email is requested, Send email by issing a POST request to the Email endpoint, setting the subject, message and customer_id in the body.\", \"expected_response\": \"Respond with a JSON object with schema_version and a resource array including: tool_type, base_url, path, method, query_params array or body, headers.\", \"query_params\": \"- JSON:API custom filtering using a filter array (e.g., filter=[{\\\"name\\\":\\\"date_shipped\\\",\\\"op\\\":\\\"gt\\\",\\\"val\\\":\\\"2023-07-14\\\"}])\", \"resources\": [{\"fields\": [\"id\", \"name\", \"balance\", \"credit_limit\"], \"filterable\": [\"id\", \"name\", \"balance\", \"credit_limit\"], \"methods\": [\"GET\", \"PATCH\", \"POST\", \"DELETE\"], \"name\": \"Customer\", \"path\": \"/Customer\"}, {\"fields\": [\"id\", \"order_id\", \"product_id\", \"quantity\", \"amount\", \"unit_price\"], \"filterable\": [\"id\", \"order_id\", \"product_id\", \"quantity\", \"amount\", \"unit_price\"], \"methods\": [\"GET\", \"PATCH\", \"POST\", \"DELETE\"], \"name\": \"Item\", \"path\": \"/Item\"}, {\"fields\": [\"id\", \"notes\", \"customer_id\", \"date_shipped\", \"amount_total\"], \"filterable\": [\"id\", \"notes\", \"customer_id\", \"date_shipped\", \"amount_total\"], \"methods\": [\"GET\", \"PATCH\", \"POST\", \"DELETE\"], \"name\": \"Order\", \"path\": \"/Order\"}, {\"fields\": [\"id\", \"name\", \"unit_price\"], \"filterable\": [\"id\", \"name\", \"unit_price\"], \"methods\": [\"GET\", \"PATCH\", \"POST\", \"DELETE\"], \"name\": \"Product\", \"path\": \"/Product\"}, {\"fields\": [\"id\", \"request\", \"request_prompt\", \"completion\"], \"filterable\": [\"id\", \"request\", \"request_prompt\", \"completion\"], \"methods\": [\"GET\", \"PATCH\", \"POST\", \"DELETE\"], \"name\": \"Mcp\", \"path\": \"/Mcp\"}], \"schema_version\": \"1.0\", \"tool_type\": \"json-api\"}\n\nNatural language query: 'List the unshipped orders created before 2023-07-14, and send a discount email (subject: 'Discount Offer') to the customer for each one.'"
11
+ }
12
+ ]
13
+
14
+ LLM request - old
15
+ [
16
+ {
17
+ "role": "system",
18
+ "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."
19
+ },
20
+ {
21
+ "role": "user",
22
+ "content": "Schema:\n{\"base_url\": \"http://localhost:5656/api\", \"description\": \"API Logic Project: basic_demo\", \"email_services\": \"iff email is requested, Send email by issing a POST request to the Email endpoint, setting the subject, message and customer_id in the body.\", \"expected_response\": \"Respond with a JSON object with schema_version and a resource array including: tool_type, base_url, path, method, query_params array or body, headers.\", \"query_params\": \"- JSON:API custom filtering using a filter array (e.g., filter=[{\\\"name\\\":\\\"date_shipped\\\",\\\"op\\\":\\\"gt\\\",\\\"val\\\":\\\"2023-07-14\\\"}])\", \"resources\": [{\"fields\": [\"id\", \"name\", \"balance\", \"credit_limit\"], \"filterable\": [\"id\", \"name\", \"balance\", \"credit_limit\"], \"methods\": [\"GET\", \"PATCH\", \"POST\", \"DELETE\"], \"name\": \"Customer\", \"path\": \"/Customer\"}, {\"fields\": [\"id\", \"order_id\", \"product_id\", \"quantity\", \"amount\", \"unit_price\"], \"filterable\": [\"id\", \"order_id\", \"product_id\", \"quantity\", \"amount\", \"unit_price\"], \"methods\": [\"GET\", \"PATCH\", \"POST\", \"DELETE\"], \"name\": \"Item\", \"path\": \"/Item\"}, {\"fields\": [\"id\", \"notes\", \"customer_id\", \"date_shipped\", \"amount_total\"], \"filterable\": [\"id\", \"notes\", \"customer_id\", \"date_shipped\", \"amount_total\"], \"methods\": [\"GET\", \"PATCH\", \"POST\", \"DELETE\"], \"name\": \"Order\", \"path\": \"/Order\"}, {\"fields\": [\"id\", \"name\", \"unit_price\"], \"filterable\": [\"id\", \"name\", \"unit_price\"], \"methods\": [\"GET\", \"PATCH\", \"POST\", \"DELETE\"], \"name\": \"Product\", \"path\": \"/Product\"}, {\"fields\": [\"id\", \"request\", \"request_prompt\", \"completion\"], \"filterable\": [\"id\", \"request\", \"request_prompt\", \"completion\"], \"methods\": [\"GET\", \"PATCH\", \"POST\", \"DELETE\"], \"name\": \"Mcp\", \"path\": \"/Mcp\"}], \"schema_version\": \"1.0\", \"tool_type\": \"json-api\"}\n\nNatural language query: 'List the unshipped orders created before 2023-07-14, and send a discount email (subject: 'Discount Offer') to the customer for each one.'"
23
+ }
24
+ ]
25
+
26
+
27
+ messages - new
28
+ [{'role': 'system', 'content': 'You are an API planner that converts natural language queries into MCP Tool Context b...API. Return only the tool context as JSON.'}, {'role': 'user', 'content': 'Schema:\n{"base_url": "http://localhost:5656/api", "description": "API Logic Project: ...) to the customer for each one.'"}]
29
+
30
+ messages- old
31
+ [{'role': 'system', '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.'}, {'role': 'user', 'content': 'Schema:\n{"base_url": "http://localhost:5656/api", "description": "API Logic Project: basic_demo", "email_services": "iff email is requested, Send email by issing a POST request to the Email endpoint, setting the subject, message and customer_id in the body.", "expected_response": "Respond with a JSON object with schema_version and a resource array including: tool_type, base_url, path, method, query_params array or body, headers.", "query_params": "- JSON:API custom filtering using a filter array (e.g., filter=[{\\"name\\":\\"date_shipped\\",\\"op\\":\\"gt\\",\\"val\\":\\"2023-07-14\\"}])", "resources": [{"fields": ["id", "name", "balance", "credit_limit"], "filterable": ["id", "name", "balance", "credit_limit"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Customer", "path": "/Customer"}, {"fields": ["id", "order_id", "product_id", "quantity", "amount", "unit_price"], "filterable": ["id", "order_id", "product_id", "quantity", "amount", "unit_price"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Item", "path": "/Item"}, {"fields": ["id", "notes", "customer_id", "date_shipped", "amount_total"], "filterable": ["id", "notes", "customer_id", "date_shipped", "amount_total"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Order", "path": "/Order"}, {"fields": ["id", "name", "unit_price"], "filterable": ["id", "name", "unit_price"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Product", "path": "/Product"}, {"fields": ["id", "request", "request_prompt", "completion"], "filterable": ["id", "request", "request_prompt", "completion"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Mcp", "path": "/Mcp"}], "schema_version": "1.0", "tool_type": "json-api"}\n\nNatural language query: \'List the unshipped orders created before 2023-07-14, and send a discount email (subject: \'Discount Offer\') to the customer for each one.\''}]
32
+
33
+ old content
34
+ 'Schema:\n{"base_url": "http://localhost:5656/api", "description": "API Logic Project: basic_demo", "email_services": "iff email is requested, Send email by issing a POST request to the Email endpoint, setting the subject, message and customer_id in the body.", "expected_response": "Respond with a JSON object with schema_version and a resource array including: tool_type, base_url, path, method, query_params array or body, headers.", "query_params": "- JSON:API custom filtering using a filter array (e.g., filter=[{\\"name\\":\\"date_shipped\\",\\"op\\":\\"gt\\",\\"val\\":\\"2023-07-14\\"}])", "resources": [{"fields": ["id", "name", "balance", "credit_limit"], "filterable": ["id", "name", "balance", "credit_limit"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Customer", "path": "/Customer"}, {"fields": ["id", "order_id", "product_id", "quantity", "amount", "unit_price"], "filterable": ["id", "order_id", "product_id", "quantity", "amount", "unit_price"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Item", "path": "/Item"}, {"fields": ["id", "notes", "customer_id", "date_shipped", "amount_total"], "filterable": ["id", "notes", "customer_id", "date_shipped", "amount_total"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Order", "path": "/Order"}, {"fields": ["id", "name", "unit_price"], "filterable": ["id", "name", "unit_price"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Product", "path": "/Product"}, {"fields": ["id", "request", "request_prompt", "completion"], "filterable": ["id", "request", "request_prompt", "completion"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Mcp", "path": "/Mcp"}], "schema_version": "1.0", "tool_type": "json-api"}\n\nNatural language query: \'List the unshipped orders created before 2023-07-14, and send a discount email (subject: \'Discount Offer\') to the customer for each one.\''
35
+
36
+ where new content looks correct:
37
+ 'Schema:\n{"base_url": "http://localhost:5656/api", "description": "API Logic Project: basic_demo", "email_services": "iff email is requested, Send email by issing a POST request to the Email endpoint, setting the subject, message and customer_id in the body.", "expected_response": "Respond with a JSON object with schema_version and a resource array including: tool_type, base_url, path, method, query_params array or body, headers.", "query_params": "- JSON:API custom filtering using a filter array (e.g., filter=[{\\"name\\":\\"date_shipped\\",\\"op\\":\\"gt\\",\\"val\\":\\"2023-07-14\\"}])", "resources": [{"fields": ["id", "name", "balance", "credit_limit"], "filterable": ["id", "name", "balance", "credit_limit"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Customer", "path": "/Customer"}, {"fields": ["id", "order_id", "product_id", "quantity", "amount", "unit_price"], "filterable": ["id", "order_id", "product_id", "quantity", "amount", "unit_price"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Item", "path": "/Item"}, {"fields": ["id", "notes", "customer_id", "date_shipped", "amount_total"], "filterable": ["id", "notes", "customer_id", "date_shipped", "amount_total"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Order", "path": "/Order"}, {"fields": ["id", "name", "unit_price"], "filterable": ["id", "name", "unit_price"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Product", "path": "/Product"}, {"fields": ["id", "request", "request_prompt", "completion"], "filterable": ["id", "request", "request_prompt", "completion"], "methods": ["GET", "PATCH", "POST", "DELETE"], "name": "Mcp", "path": "/Mcp"}], "schema_version": "1.0", "tool_type": "json-api"}\n\nNatural language query: \'List the unshipped orders created before 2023-07-14, and send a discount email (subject: \'Discount Offer\') to the customer for each one.\''