euri10: @florimondmanca should I write a test for it @florimondmanca ? seems a little bit useless to me but if you want
florimondmanca: Yes ideally let's add a test to prevent regressions. Surely there's somewhere we test the content of `scope`?
euri10: I put the latest master changes, which caught some small stuff with flake8 !

I think it should be ok now, would be great if you have time to take a look at it @florimondmanca 
diff --git a/uvicorn/config.py b/uvicorn/config.py
index 331f738..c35b567 100644
--- a/uvicorn/config.py
+++ b/uvicorn/config.py
@@ -200,6 +200,10 @@ class Config:
         else:
             self.forwarded_allow_ips = forwarded_allow_ips
 
+    @property
+    def asgi_version(self) -> str:
+        return {"asgi2": "2.0", "asgi3": "3.0"}[self.interface]
+
     @property
     def is_ssl(self) -> bool:
         return bool(self.ssl_keyfile or self.ssl_certfile)
diff --git a/uvicorn/protocols/http/h11_impl.py b/uvicorn/protocols/http/h11_impl.py
index a1d87eb..5e15d05 100644
--- a/uvicorn/protocols/http/h11_impl.py
+++ b/uvicorn/protocols/http/h11_impl.py
@@ -188,6 +188,10 @@ class H11Protocol(asyncio.Protocol):
                 raw_path, _, query_string = event.target.partition(b"?")
                 self.scope = {
                     "type": "http",
+                    "asgi": {
+                        "version": self.config.asgi_version,
+                        "spec_version": "2.1",
+                    },
                     "http_version": event.http_version.decode("ascii"),
                     "server": self.server,
                     "client": self.client,
diff --git a/uvicorn/protocols/http/httptools_impl.py b/uvicorn/protocols/http/httptools_impl.py
index 6e53131..7a60733 100644
--- a/uvicorn/protocols/http/httptools_impl.py
+++ b/uvicorn/protocols/http/httptools_impl.py
@@ -214,6 +214,7 @@ class HttpToolsProtocol(asyncio.Protocol):
         self.headers = []
         self.scope = {
             "type": "http",
+            "asgi": {"version": self.config.asgi_version, "spec_version": "2.1"},
             "http_version": "1.1",
             "server": self.server,
             "client": self.client,
diff --git a/uvicorn/protocols/websockets/websockets_impl.py b/uvicorn/protocols/websockets/websockets_impl.py
index 53f2d42..37e2cb7 100644
--- a/uvicorn/protocols/websockets/websockets_impl.py
+++ b/uvicorn/protocols/websockets/websockets_impl.py
@@ -100,6 +100,7 @@ class WebSocketProtocol(websockets.WebSocketServerProtocol):
 
         self.scope = {
             "type": "websocket",
+            "asgi": {"version": self.config.asgi_version, "spec_version": "2.1"},
             "scheme": self.scheme,
             "server": self.server,
             "client": self.client,
diff --git a/uvicorn/protocols/websockets/wsproto_impl.py b/uvicorn/protocols/websockets/wsproto_impl.py
index 5081c87..307ab9f 100644
--- a/uvicorn/protocols/websockets/wsproto_impl.py
+++ b/uvicorn/protocols/websockets/wsproto_impl.py
@@ -128,6 +128,7 @@ class WSProtocol(asyncio.Protocol):
         raw_path, _, query_string = event.target.partition("?")
         self.scope = {
             "type": "websocket",
+            "asgi": {"version": self.config.asgi_version, "spec_version": "2.1"},
             "http_version": "1.1",
             "scheme": self.scheme,
             "server": self.server,

HTTP Scope missing fields
https://asgi.readthedocs.io/en/latest/specs/www.html#connection-scope

>The connection scope contains:
    ...
    asgi["version"] (Unicode string) – Version of the ASGI spec.
    asgi["spec_version"] (Unicode string) – Version of the ASGI HTTP spec this server understands; one of "2.0" or "2.1". Optional; if missing assume 2.0.

I sent a request through uvicorn and noticed these fields are missing. Additionally searching the code for "spec_version" does not return any hits so I think these fields are not being handled. Potentially these were added in a later ASGI version that is not supported or I may be misunderstanding what the ASGI docs mean.
diff --git a/tests/protocols/test_http.py b/tests/protocols/test_http.py
index 3900e3b..891a259 100644
--- a/tests/protocols/test_http.py
+++ b/tests/protocols/test_http.py
@@ -674,3 +674,29 @@ def test_supported_upgrade_request(protocol_cls):
     protocol.data_received(UPGRADE_REQUEST)
 
     assert b"HTTP/1.1 426 " in protocol.transport.buffer
+
+
+async def asgi3app(scope, receive, send):
+    pass
+
+
+def asgi2app(scope):
+    async def asgi(receive, send):
+        pass
+
+    return asgi
+
+
+asgi_scope_data = [
+    (asgi3app, {"version": "3.0", "spec_version": "2.1"}),
+    (asgi2app, {"version": "2.0", "spec_version": "2.1"}),
+]
+
+
+@pytest.mark.parametrize("asgi2or3_app, expected_scopes", asgi_scope_data)
+@pytest.mark.parametrize("protocol_cls", HTTP_PROTOCOLS)
+def test_scopes(asgi2or3_app, expected_scopes, protocol_cls):
+    protocol = get_connected_protocol(asgi2or3_app, protocol_cls,)
+    protocol.data_received(SIMPLE_GET_REQUEST)
+    protocol.loop.run_one()
+    assert expected_scopes == protocol.scope.get("asgi")
diff --git a/tests/test_config.py b/tests/test_config.py
index 1f3d34e..c1f763b 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -66,3 +66,19 @@ def test_ssl_config(certfile_and_keyfile):
     config.load()
 
     assert config.is_ssl is True
+
+
+def asgi2_app(scope):
+    async def asgi(receive, send):
+        pass
+
+    return asgi
+
+
+@pytest.mark.parametrize(
+    "app, expected_interface", [(asgi_app, "3.0",), (asgi2_app, "2.0",)]
+)
+def test_asgi_version(app, expected_interface):
+    config = Config(app=app)
+    config.load()
+    assert config.asgi_version == expected_interface

pip install -e .
parse_log_pytest
requirements.txt
apt-get update apt-get install -y gcc
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
autoflake==2.3.1
babel==2.17.0
backrefs==5.8
black==25.1.0
certifi==2025.1.31
charset-normalizer==3.4.1
click==7.1.2
codecov==2.1.13
colorama==0.4.6
coverage==7.8.0
exceptiongroup==1.2.2
flake8==7.2.0
ghp-import==2.1.0
h11==0.8.1
httptools==0.1.2
idna==3.10
importlib_metadata==8.6.1
iniconfig==2.1.0
isort==6.0.1
Jinja2==3.1.6
Markdown==3.7
MarkupSafe==3.0.2
mccabe==0.7.0
mergedeep==1.3.4
mkdocs==1.6.1
mkdocs-get-deps==0.2.0
mkdocs-material==9.6.10
mkdocs-material-extensions==1.3.1
mypy-extensions==1.0.0
packaging==24.2
paginate==0.5.7
pathspec==0.12.1
platformdirs==4.3.7
pluggy==1.5.0
pycodestyle==2.13.0
pyflakes==3.3.2
Pygments==2.19.1
pymdown-extensions==10.14.3
pytest==8.3.5
pytest-cov==6.0.0
python-dateutil==2.9.0.post0
PyYAML==6.0.2
pyyaml_env_tag==0.1
requests==2.32.3
six==1.17.0
tomli==2.2.1
typing_extensions==4.13.0
urllib3==2.3.0
-e git+https://github.com/encode/uvicorn.git@779fcf7547e14a49259063ca05be919453883a60#egg=uvicorn
uvloop==0.21.0
watchdog==6.0.0
watchgod==0.6
websockets==8.1
wsproto==0.13.0
zipp==3.21.0

name: uvicorn
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - autoflake==2.3.1
      - babel==2.17.0
      - backrefs==5.8
      - black==25.1.0
      - certifi==2025.1.31
      - charset-normalizer==3.4.1
      - click==7.1.2
      - codecov==2.1.13
      - colorama==0.4.6
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - flake8==7.2.0
      - ghp-import==2.1.0
      - h11==0.8.1
      - httptools==0.1.2
      - idna==3.10
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - isort==6.0.1
      - jinja2==3.1.6
      - markdown==3.7
      - markupsafe==3.0.2
      - mccabe==0.7.0
      - mergedeep==1.3.4
      - mkdocs==1.6.1
      - mkdocs-get-deps==0.2.0
      - mkdocs-material==9.6.10
      - mkdocs-material-extensions==1.3.1
      - mypy-extensions==1.0.0
      - packaging==24.2
      - paginate==0.5.7
      - pathspec==0.12.1
      - platformdirs==4.3.7
      - pluggy==1.5.0
      - pycodestyle==2.13.0
      - pyflakes==3.3.2
      - pygments==2.19.1
      - pymdown-extensions==10.14.3
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - python-dateutil==2.9.0.post0
      - pyyaml==6.0.2
      - pyyaml-env-tag==0.1
      - requests==2.32.3
      - six==1.17.0
      - tomli==2.2.1
      - typing-extensions==4.13.0
      - urllib3==2.3.0
      - uvloop==0.21.0
      - watchdog==6.0.0
      - watchgod==0.6
      - websockets==8.1
      - wsproto==0.13.0
      - zipp==3.21.0
prefix: /opt/conda/envs/uvicorn

tests/protocols/test_http.py::test_scopes[H11Protocol-asgi3app-expected_scopes0] tests/protocols/test_http.py::test_scopes[H11Protocol-asgi2app-expected_scopes1] tests/protocols/test_http.py::test_scopes[HttpToolsProtocol-asgi3app-expected_scopes0] tests/protocols/test_http.py::test_scopes[HttpToolsProtocol-asgi2app-expected_scopes1] tests/test_config.py::test_asgi_version[asgi_app-3.0] tests/test_config.py::test_asgi_version[asgi2_app-2.0]
tests/protocols/test_http.py::test_get_request[H11Protocol] tests/protocols/test_http.py::test_get_request[HttpToolsProtocol] tests/protocols/test_http.py::test_request_logging[H11Protocol-/] tests/protocols/test_http.py::test_request_logging[H11Protocol-/?foo] tests/protocols/test_http.py::test_request_logging[H11Protocol-/?foo=bar] tests/protocols/test_http.py::test_request_logging[H11Protocol-/?foo=bar&baz=1] tests/protocols/test_http.py::test_request_logging[HttpToolsProtocol-/] tests/protocols/test_http.py::test_request_logging[HttpToolsProtocol-/?foo] tests/protocols/test_http.py::test_request_logging[HttpToolsProtocol-/?foo=bar] tests/protocols/test_http.py::test_request_logging[HttpToolsProtocol-/?foo=bar&baz=1] tests/protocols/test_http.py::test_head_request[H11Protocol] tests/protocols/test_http.py::test_head_request[HttpToolsProtocol] tests/protocols/test_http.py::test_post_request[H11Protocol] tests/protocols/test_http.py::test_post_request[HttpToolsProtocol] tests/protocols/test_http.py::test_keepalive[H11Protocol] tests/protocols/test_http.py::test_keepalive[HttpToolsProtocol] tests/protocols/test_http.py::test_keepalive_timeout[H11Protocol] tests/protocols/test_http.py::test_keepalive_timeout[HttpToolsProtocol] tests/protocols/test_http.py::test_close[H11Protocol] tests/protocols/test_http.py::test_close[HttpToolsProtocol] tests/protocols/test_http.py::test_chunked_encoding[H11Protocol] tests/protocols/test_http.py::test_chunked_encoding[HttpToolsProtocol] tests/protocols/test_http.py::test_chunked_encoding_empty_body[H11Protocol] tests/protocols/test_http.py::test_chunked_encoding_empty_body[HttpToolsProtocol] tests/protocols/test_http.py::test_chunked_encoding_head_request[H11Protocol] tests/protocols/test_http.py::test_chunked_encoding_head_request[HttpToolsProtocol] tests/protocols/test_http.py::test_pipelined_requests[H11Protocol] tests/protocols/test_http.py::test_pipelined_requests[HttpToolsProtocol] tests/protocols/test_http.py::test_undersized_request[H11Protocol] tests/protocols/test_http.py::test_undersized_request[HttpToolsProtocol] tests/protocols/test_http.py::test_oversized_request[H11Protocol] tests/protocols/test_http.py::test_oversized_request[HttpToolsProtocol] tests/protocols/test_http.py::test_large_post_request[H11Protocol] tests/protocols/test_http.py::test_large_post_request[HttpToolsProtocol] tests/protocols/test_http.py::test_invalid_http[H11Protocol] tests/protocols/test_http.py::test_invalid_http[HttpToolsProtocol] tests/protocols/test_http.py::test_app_exception[H11Protocol] tests/protocols/test_http.py::test_app_exception[HttpToolsProtocol] tests/protocols/test_http.py::test_exception_during_response[H11Protocol] tests/protocols/test_http.py::test_exception_during_response[HttpToolsProtocol] tests/protocols/test_http.py::test_no_response_returned[H11Protocol] tests/protocols/test_http.py::test_no_response_returned[HttpToolsProtocol] tests/protocols/test_http.py::test_partial_response_returned[H11Protocol] tests/protocols/test_http.py::test_partial_response_returned[HttpToolsProtocol] tests/protocols/test_http.py::test_duplicate_start_message[H11Protocol] tests/protocols/test_http.py::test_duplicate_start_message[HttpToolsProtocol] tests/protocols/test_http.py::test_missing_start_message[H11Protocol] tests/protocols/test_http.py::test_missing_start_message[HttpToolsProtocol] tests/protocols/test_http.py::test_message_after_body_complete[H11Protocol] tests/protocols/test_http.py::test_message_after_body_complete[HttpToolsProtocol] tests/protocols/test_http.py::test_value_returned[H11Protocol] tests/protocols/test_http.py::test_value_returned[HttpToolsProtocol] tests/protocols/test_http.py::test_early_disconnect[H11Protocol] tests/protocols/test_http.py::test_early_disconnect[HttpToolsProtocol] tests/protocols/test_http.py::test_early_response[H11Protocol] tests/protocols/test_http.py::test_early_response[HttpToolsProtocol] tests/protocols/test_http.py::test_read_after_response[H11Protocol] tests/protocols/test_http.py::test_read_after_response[HttpToolsProtocol] tests/protocols/test_http.py::test_http10_request[H11Protocol] tests/protocols/test_http.py::test_http10_request[HttpToolsProtocol] tests/protocols/test_http.py::test_root_path[H11Protocol] tests/protocols/test_http.py::test_root_path[HttpToolsProtocol] tests/protocols/test_http.py::test_raw_path[H11Protocol] tests/protocols/test_http.py::test_raw_path[HttpToolsProtocol] tests/protocols/test_http.py::test_max_concurrency[H11Protocol] tests/protocols/test_http.py::test_max_concurrency[HttpToolsProtocol] tests/protocols/test_http.py::test_shutdown_during_request[H11Protocol] tests/protocols/test_http.py::test_shutdown_during_request[HttpToolsProtocol] tests/protocols/test_http.py::test_shutdown_during_idle[H11Protocol] tests/protocols/test_http.py::test_shutdown_during_idle[HttpToolsProtocol] tests/protocols/test_http.py::test_100_continue_sent_when_body_consumed[H11Protocol] tests/protocols/test_http.py::test_100_continue_sent_when_body_consumed[HttpToolsProtocol] tests/protocols/test_http.py::test_100_continue_not_sent_when_body_not_consumed[H11Protocol] tests/protocols/test_http.py::test_100_continue_not_sent_when_body_not_consumed[HttpToolsProtocol] tests/protocols/test_http.py::test_unsupported_upgrade_request[H11Protocol] tests/protocols/test_http.py::test_unsupported_upgrade_request[HttpToolsProtocol] tests/protocols/test_http.py::test_supported_upgrade_request[H11Protocol] tests/protocols/test_http.py::test_supported_upgrade_request[HttpToolsProtocol] tests/test_config.py::test_debug_app tests/test_config.py::test_wsgi_app tests/test_config.py::test_proxy_headers tests/test_config.py::test_app_unimportable tests/test_config.py::test_concrete_http_class tests/test_config.py::test_socket_bind tests/test_config.py::test_ssl_config
uvicorn/protocols/websockets/wsproto_impl.py uvicorn/config.py uvicorn/protocols/http/httptools_impl.py uvicorn/protocols/http/h11_impl.py uvicorn/protocols/websockets/websockets_impl.py
uvicorn/protocols/websockets/wsproto_impl.py uvicorn/config.py uvicorn/protocols/http/httptools_impl.py uvicorn/protocols/http/h11_impl.py uvicorn/protocols/websockets/websockets_impl.py