jerryshao commented on code in PR #14208:
URL: https://github.com/apache/gravitino/pull/14208#discussion_r4409589089


##########
mcp-server/mcp_server/client/plain/utils.py:
##########
@@ -37,6 +39,31 @@ def extract_content_from_response(response, field: str, 
default="") -> str:
     return json.dumps(response_json.get(field, default))
 
 
+def extract_list_from_response(response, field: str) -> str:
+    """Extract a required JSON array without rounding fractional numbers.
+
+    Propagate server errors and reject malformed envelopes instead of returning
+    an empty result for a failed discovery request.
+    """
+    try:
+        payload = simplejson.loads(response.content, use_decimal=True)
+    except simplejson.JSONDecodeError as exc:

Review Comment:
   [Nit] A body that is not valid UTF-8 escapes this normalization. 
`simplejson.loads()` on a `bytes` argument decodes before parsing, so invalid 
bytes raise `UnicodeDecodeError`, not `simplejson.JSONDecodeError`. That 
exception misses this `except`, so the caller gets a raw `UnicodeDecodeError` 
instead of the `Invalid Gravitino response: invalid JSON` message added in 
b71162e7 and, because `response.raise_for_status()` on line 51 is skipped too, 
a non-2xx response with such a body reports the decode error rather than its 
HTTP status.
   
   Widening the clause covers both: `except (simplejson.JSONDecodeError, 
UnicodeDecodeError) as exc:`. (`extract_content_from_response` is not affected 
the same way, since `response.json()` applies httpx charset handling.)
   
   Verified by: ran `simplejson.loads(b'\xff\xfe{"code":0}', use_decimal=True)` 
in this PR's `uv --locked` environment and got `UnicodeDecodeError: 'utf-8' 
codec can't decode byte 0xff in position 0`, which the current clause does not 
catch.



##########
mcp-server/mcp_server/client/plain/utils.py:
##########
@@ -37,6 +39,31 @@ def extract_content_from_response(response, field: str, 
default="") -> str:
     return json.dumps(response_json.get(field, default))
 
 
+def extract_list_from_response(response, field: str) -> str:
+    """Extract a required JSON array without rounding fractional numbers.
+
+    Propagate server errors and reject malformed envelopes instead of returning
+    an empty result for a failed discovery request.
+    """
+    try:
+        payload = simplejson.loads(response.content, use_decimal=True)
+    except simplejson.JSONDecodeError as exc:
+        response.raise_for_status()
+        raise GravitinoException(
+            "Invalid Gravitino response: invalid JSON"
+        ) from exc
+    if isinstance(payload, dict):
+        _handle_gravitino_exception(payload)
+    response.raise_for_status()
+    if not isinstance(payload, dict) or "code" not in payload:
+        raise GravitinoException("Invalid Gravitino response: missing code")
+    if not isinstance(payload.get(field), list):
+        raise GravitinoException(
+            f"Invalid Gravitino response: {field} must be a JSON array"
+        )
+    return simplejson.dumps(payload[field], use_decimal=True)

Review Comment:
   [Question] Is the precision guarantee meant to stay limited to these two 
tools? `extract_content_from_response` and `extract_response` (lines 36-39 and 
67-71, unchanged) still use stdlib `json`, so `load_semantic_model` returns the 
*same* metric definitions with `aiContext` numbers rounded to float, while 
`list_metric_infos` returns them exactly. An agent that lists metrics and then 
loads the containing model sees two different values for one stored number.
   
   The server keeps the full precision in both cases 
(`SemanticDTOUtils.java:88-91` reads them as `BigInteger`/`BigDecimal`), so 
this is purely a client-side difference. Routing the other two extractors 
through `simplejson.loads(..., use_decimal=True)` would make it uniform; if you 
would rather keep this PR to metric discovery, a follow-up issue would be 
enough.
   
   Verified by: read all three extractor functions in `utils.py` on this branch 
and confirmed `load_semantic_model` / `create_semantic_model` still call the 
stdlib-`json` extractors at 
`plain_rest_client_semantic_model_operation.py:67,87,100,108`.



##########
mcp-server/mcp_server/tools/semantic_model.py:
##########
@@ -43,6 +44,65 @@ async def list_of_semantic_models(
             )
         )
 
+    @mcp.tool(
+        tags={"semantic_model"},
+        annotations=ToolAnnotations(
+            readOnlyHint=True, destructiveHint=False, idempotentHint=True

Review Comment:
   [Nit] These are the only two tools in the whole MCP server carrying 
`ToolAnnotations`. A client that filters or auto-approves on `readOnlyHint` 
will treat the equally read-only `list_of_semantic_models` and 
`load_semantic_model` (and every `list_of_*` / `load_*` tool in the other 
modules) as unannotated, which the spec says to read as the conservative 
default. The asymmetry makes the hint less useful than if nothing were 
annotated.
   
   Worth either annotating the other read-only semantic-model tools in this PR, 
or filing a follow-up to annotate the read-only tools repo-wide so the hint 
means something consistent.
   
   Verified by: `grep -rn "ToolAnnotations\|readOnlyHint" 
mcp-server/mcp_server/` returns only these two tool definitions (lines 49-50 
and 78-79 of this file).



##########
docs/gravitino-mcp-server.md:
##########
@@ -139,6 +139,8 @@ Gravitino MCP server supports the following tools, and you 
could export tool by
 | `list_of_views`                     | Retrieve a list of views for a schema. 
Only for catalogs supporting views.     | `view`          |
 | `load_view`                         | Retrieve a view's metadata. Only for 
catalogs supporting views.                | `view`          |
 | `list_of_semantic_models`           | List the Semantic Models the caller 
can access in a schema.                    | `semantic_model` |
+| `list_metrics`                      | List visible metric identifiers across 
Semantic Models in a schema.           | `semantic_model` |
+| `list_metric_infos`                 | List complete visible metrics across 
Semantic Models in a schema.             | `semantic_model` |

Review Comment:
   [Nit] One more doc page enumerates these tools and was not updated. 
`docs/manage-semantic-model-metadata-using-gravitino.md:592-594` still reads 
"provides `list_of_semantic_models`, `load_semantic_model`, 
`create_semantic_model`, `alter_semantic_model`, and `drop_semantic_model`" and 
links into this page's `#semantic-models` anchor, so it now contradicts the 
table above. Adding the two new tool names there keeps the two pages consistent 
(this PR already dropped the matching "these five tools" wording on line 154 of 
this file).
   
   Verified by: grepped the repo for `list_of_semantic_models` outside 
`mcp-server/`; that file is the only other doc enumerating the tool set, and 
`git diff --stat origin/main...HEAD` shows it is untouched by this PR.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to