|
1 | 1 | """Tests for `mcp.shared.extension` — the extension-identifier grammar shared by |
2 | | -the server and client extension surfaces. |
| 2 | +the server and client extension surfaces.""" |
3 | 3 |
|
4 | | -The grammar matrix (accepted and rejected identifiers) lives with the original |
5 | | -server tests in `tests/server/mcpserver/test_extension.py`, which exercise the |
6 | | -same function via the server module's re-export. |
7 | | -""" |
| 4 | +from typing import Any |
8 | 5 |
|
9 | 6 | import pytest |
10 | 7 |
|
11 | 8 | import mcp.server.extension |
12 | 9 | import mcp.shared.extension |
13 | | - |
14 | | - |
15 | | -def test_validator_importable_from_shared_home() -> None: |
16 | | - """SDK-defined: the identifier grammar lives in `mcp.shared.extension` — one |
17 | | - source of truth for both the server and client extension surfaces.""" |
18 | | - mcp.shared.extension.validate_extension_identifier("com.example/thing", owner="T") |
19 | | - |
20 | | - |
21 | | -def test_validator_rejects_malformed_identifier_via_shared_path() -> None: |
22 | | - """SDK-defined: the shared-home function enforces the same `vendor-prefix/name` |
23 | | - grammar the server side always has.""" |
24 | | - with pytest.raises(TypeError): |
25 | | - mcp.shared.extension.validate_extension_identifier("noprefix", owner="T") |
| 10 | +from mcp.shared.extension import validate_extension_identifier |
26 | 11 |
|
27 | 12 |
|
28 | 13 | def test_server_extension_module_reexports_shared_validator() -> None: |
29 | 14 | """SDK-defined: `mcp.server.extension.validate_extension_identifier` remains |
30 | 15 | importable after the move and is the very same function object.""" |
31 | 16 | assert mcp.server.extension.validate_extension_identifier is mcp.shared.extension.validate_extension_identifier |
| 17 | + |
| 18 | + |
| 19 | +@pytest.mark.parametrize( |
| 20 | + "identifier", |
| 21 | + [ |
| 22 | + "io.modelcontextprotocol/ui", |
| 23 | + "com.example/my_ext", |
| 24 | + "com.x-y.z2/n.a-b_c", |
| 25 | + "example/x", |
| 26 | + "a/b", |
| 27 | + "com.example/9start", |
| 28 | + ], |
| 29 | +) |
| 30 | +def test_grammar_conformant_extension_identifiers_are_accepted(identifier: str) -> None: |
| 31 | + """Spec `_meta` key grammar: dot-separated labels (letter start, letter/digit end, |
| 32 | + hyphens interior), a slash, then a name that starts and ends alphanumeric.""" |
| 33 | + validate_extension_identifier(identifier, owner="T") |
| 34 | + |
| 35 | + |
| 36 | +@pytest.mark.parametrize( |
| 37 | + "identifier", |
| 38 | + [ |
| 39 | + "noprefix", |
| 40 | + "-foo/bar", |
| 41 | + ".leading/x", |
| 42 | + "a..b/x", |
| 43 | + "foo-/x", |
| 44 | + "9foo/x", |
| 45 | + "foo/-bar", |
| 46 | + "foo/bar-", |
| 47 | + "foo/", |
| 48 | + "/bar", |
| 49 | + "foo/ba r", |
| 50 | + "io.modelcontextprotocol/ui\n", |
| 51 | + "", |
| 52 | + None, |
| 53 | + 42, |
| 54 | + ], |
| 55 | +) |
| 56 | +def test_malformed_extension_identifiers_are_rejected(identifier: Any) -> None: |
| 57 | + """Spec `_meta` key grammar: malformed prefixes (bad label start/end, empty labels) |
| 58 | + and malformed names are rejected, as are non-strings.""" |
| 59 | + with pytest.raises(TypeError): |
| 60 | + validate_extension_identifier(identifier, owner="T") |
0 commit comments