codeocean-mcp-server 0.7.1__tar.gz → 0.8.0__tar.gz
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.
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/CHANGELOG.md +8 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/PKG-INFO +7 -1
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/README.md +6 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/pyproject.toml +4 -2
- codeocean_mcp_server-0.8.0/src/codeocean_mcp_server/logging_config.py +47 -0
- codeocean_mcp_server-0.8.0/src/codeocean_mcp_server/search.py +136 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/server.py +2 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/tools/capsules.py +15 -21
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/tools/data_assets.py +15 -9
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/tests/bedrock_call.py +10 -2
- codeocean_mcp_server-0.8.0/tests/conftest.py +23 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/tests/test_multi_tool_calling.py +2 -0
- codeocean_mcp_server-0.8.0/tests/test_search.py +275 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/tests/test_tool_calling.py +1 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/uv.lock +86 -86
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/.gitignore +0 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/.python-version +0 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/LICENSE +0 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/RELEASE.md +0 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/__init__.py +0 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/file_utils.py +0 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/models.py +0 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/tools/__init__.py +0 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/tools/computations.py +0 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/tools/custom_metadata.py +0 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/tests/bedrock_tools_converter.py +0 -0
- {codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/tests/mcp_client.py +0 -0
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
CHANGELOG
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
## 0.8.0 (2026-01-27)
|
|
5
|
+
|
|
6
|
+
- [#24](https://github.com/codeocean/codeocean-mcp-server/pull/24) feat: Token efficient search responses
|
|
7
|
+
|
|
8
|
+
## 0.7.2 (2025-12-27)
|
|
9
|
+
|
|
10
|
+
- [#20](https://github.com/codeocean/codeocean-mcp-server/pull/20) feat: custom log formatting
|
|
11
|
+
|
|
4
12
|
## 0.7.1 (2024-12-23)
|
|
5
13
|
|
|
6
14
|
- [#18](https://github.com/codeocean/codeocean-mcp-server/pull/18) chore: ruff format and dep update
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeocean-mcp-server
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Code Ocean MCP Server
|
|
5
5
|
Project-URL: Homepage, https://github.com/codeocean/codeocean-mcp-server
|
|
6
6
|
Project-URL: Issues, https://github.com/codeocean/codeocean-mcp-server/issues
|
|
@@ -228,3 +228,9 @@ This will start a web server where you can:
|
|
|
228
228
|
- View available tools and resources
|
|
229
229
|
- Test tool calls interactively
|
|
230
230
|
- See server logs and responses
|
|
231
|
+
|
|
232
|
+
## Log Formatting (Optional)
|
|
233
|
+
|
|
234
|
+
The MCP server supports custom log formatting through the `LOG_FORMAT` environment variable. This allows you to control the format of log messages output by the server.
|
|
235
|
+
**Example Format Strings:** `"%(asctime)s %(levelname)s [%(name)s] %(message)s"`.
|
|
236
|
+
If `LOG_FORMAT` is not set, the server uses FastMCP's default logging configuration.
|
|
@@ -210,3 +210,9 @@ This will start a web server where you can:
|
|
|
210
210
|
- View available tools and resources
|
|
211
211
|
- Test tool calls interactively
|
|
212
212
|
- See server logs and responses
|
|
213
|
+
|
|
214
|
+
## Log Formatting (Optional)
|
|
215
|
+
|
|
216
|
+
The MCP server supports custom log formatting through the `LOG_FORMAT` environment variable. This allows you to control the format of log messages output by the server.
|
|
217
|
+
**Example Format Strings:** `"%(asctime)s %(levelname)s [%(name)s] %(message)s"`.
|
|
218
|
+
If `LOG_FORMAT` is not set, the server uses FastMCP's default logging configuration.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codeocean-mcp-server"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.8.0"
|
|
4
4
|
authors = [{ name = "Code Ocean", email = "dev@codeocean.com" }]
|
|
5
5
|
description = "Code Ocean MCP Server"
|
|
6
6
|
readme = "README.md"
|
|
@@ -18,7 +18,7 @@ license = "MIT"
|
|
|
18
18
|
|
|
19
19
|
[dependency-groups]
|
|
20
20
|
dev = [
|
|
21
|
-
"boto3>=1.42.
|
|
21
|
+
"boto3>=1.42.17",
|
|
22
22
|
"deepdiff>=8.6.1",
|
|
23
23
|
"hatch>=1.16.2",
|
|
24
24
|
"mcp-python-client>=0.1.9",
|
|
@@ -66,3 +66,5 @@ filterwarnings = [
|
|
|
66
66
|
"ignore:datetime.datetime.utcnow() is deprecated:DeprecationWarning:botocore.auth",
|
|
67
67
|
"ignore::DeprecationWarning:botocore.*",
|
|
68
68
|
]
|
|
69
|
+
markers = ["integration: requires external services (AWS/Bedrock)"]
|
|
70
|
+
asyncio_mode = "strict"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def configure_logging() -> None:
|
|
7
|
+
"""Configure logging based on LOG_FORMAT environment variable.
|
|
8
|
+
|
|
9
|
+
If LOG_FORMAT is set, configures the root logger with a StreamHandler
|
|
10
|
+
using the specified format string. This must be called before FastMCP
|
|
11
|
+
initialization to ensure our configuration takes precedence.
|
|
12
|
+
|
|
13
|
+
If LOG_FORMAT is not set or is empty, does nothing and lets FastMCP
|
|
14
|
+
configure logging with its default settings.
|
|
15
|
+
|
|
16
|
+
Environment variables:
|
|
17
|
+
LOG_FORMAT: Python logging format string (optional)
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
- "%(asctime)s agent %(levelname)s [%(name)s] %(message)s"
|
|
21
|
+
- "%(levelname)s: %(message)s"
|
|
22
|
+
- "[%(name)s] %(message)s"
|
|
23
|
+
|
|
24
|
+
Note:
|
|
25
|
+
Invalid format strings will cause errors when log records are formatted,
|
|
26
|
+
not during initialization. This typically results in ValueError, KeyError,
|
|
27
|
+
or AttributeError being raised when logging occurs.
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
log_format = os.getenv("LOG_FORMAT", "").strip()
|
|
31
|
+
|
|
32
|
+
# If LOG_FORMAT is not set or empty, do nothing
|
|
33
|
+
if not log_format:
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
# Create handler for stderr (same as FastMCP default)
|
|
37
|
+
handler = logging.StreamHandler(sys.stderr)
|
|
38
|
+
|
|
39
|
+
# Create formatter with the specified format string
|
|
40
|
+
# This will raise an error if the format string is invalid (fail fast)
|
|
41
|
+
formatter = logging.Formatter(log_format)
|
|
42
|
+
handler.setFormatter(formatter)
|
|
43
|
+
|
|
44
|
+
# Configure root logger
|
|
45
|
+
# This must be done before FastMCP calls logging.basicConfig()
|
|
46
|
+
logging.root.addHandler(handler)
|
|
47
|
+
logging.root.setLevel(logging.INFO)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import Any, ClassVar, Optional
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
# Constants
|
|
7
|
+
MAX_DESCRIPTION_LENGTH = 200
|
|
8
|
+
MAX_TAGS_COUNT = 10
|
|
9
|
+
TRUNCATION_SUFFIX = "...(more)"
|
|
10
|
+
TAGS_TRUNCATION_MARKER = "..more.."
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def truncate_description(description: Optional[str], max_length: int = MAX_DESCRIPTION_LENGTH) -> Optional[str]:
|
|
14
|
+
"""Truncate description with word-boundary-aware cutting."""
|
|
15
|
+
if not description:
|
|
16
|
+
return None
|
|
17
|
+
|
|
18
|
+
# Light whitespace normalization: collapse 3+ consecutive whitespace to single space.
|
|
19
|
+
normalized = re.sub(r"\s{3,}", " ", description).strip()
|
|
20
|
+
if not normalized:
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
if len(normalized) <= max_length:
|
|
24
|
+
return normalized
|
|
25
|
+
|
|
26
|
+
truncate_at = max_length - len(TRUNCATION_SUFFIX)
|
|
27
|
+
if truncate_at <= 0:
|
|
28
|
+
return TRUNCATION_SUFFIX[:max_length]
|
|
29
|
+
|
|
30
|
+
last_space = normalized.rfind(" ", 0, truncate_at)
|
|
31
|
+
cut_at = last_space if last_space > max_length // 2 else truncate_at
|
|
32
|
+
return normalized[:cut_at].rstrip() + TRUNCATION_SUFFIX
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def limit_tags(tags: Optional[list[str]], max_count: int = MAX_TAGS_COUNT) -> list[str]:
|
|
36
|
+
"""Limit tags to maximum count with truncation marker."""
|
|
37
|
+
if not tags:
|
|
38
|
+
return []
|
|
39
|
+
|
|
40
|
+
if len(tags) <= max_count:
|
|
41
|
+
return list(tags)
|
|
42
|
+
|
|
43
|
+
return list(tags[: max_count - 1]) + [TAGS_TRUNCATION_MARKER]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class CompactCapsuleItem(BaseModel):
|
|
47
|
+
"""Compact capsule/pipeline item (id kept, other fields shortened)."""
|
|
48
|
+
|
|
49
|
+
id: str
|
|
50
|
+
n: str
|
|
51
|
+
s: str
|
|
52
|
+
d: Optional[str] = None
|
|
53
|
+
t: list[str]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class CompactDataAssetItem(BaseModel):
|
|
57
|
+
"""Compact data asset item (id kept, other fields shortened)."""
|
|
58
|
+
|
|
59
|
+
id: str
|
|
60
|
+
n: str
|
|
61
|
+
d: Optional[str] = None
|
|
62
|
+
t: list[str]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class SearchMeta(BaseModel):
|
|
66
|
+
"""Base model providing search metadata fields."""
|
|
67
|
+
|
|
68
|
+
has_more: bool
|
|
69
|
+
next_token: Optional[str] = None
|
|
70
|
+
item_count: int
|
|
71
|
+
field_names: Optional[dict[str, str]] = None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class CapsuleSearchResults(SearchMeta):
|
|
75
|
+
"""Compact results: {items: [{id, n, s, d, t}], has_more, next_token, total_count}.
|
|
76
|
+
|
|
77
|
+
Fields: id=id, n=name, s=slug, d=description (truncated), t=tags (limited).
|
|
78
|
+
Set include_field_names=true to add field_names with full labels.
|
|
79
|
+
Use get_capsule(id) if full details needed.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
items: list[CompactCapsuleItem]
|
|
83
|
+
FIELD_NAMES: ClassVar[dict[str, str]] = {"id": "id", "n": "name", "s": "slug", "d": "description", "t": "tags"}
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def from_sdk_results(cls, sdk_results: Any, include_field_names: bool = False) -> "CapsuleSearchResults":
|
|
87
|
+
"""Convert SDK search results to compact format."""
|
|
88
|
+
items = [
|
|
89
|
+
CompactCapsuleItem(
|
|
90
|
+
id=c.id,
|
|
91
|
+
n=c.name,
|
|
92
|
+
s=c.slug,
|
|
93
|
+
d=truncate_description(c.description),
|
|
94
|
+
t=limit_tags(c.tags),
|
|
95
|
+
)
|
|
96
|
+
for c in sdk_results.results
|
|
97
|
+
]
|
|
98
|
+
return cls(
|
|
99
|
+
items=items,
|
|
100
|
+
has_more=sdk_results.has_more,
|
|
101
|
+
next_token=getattr(sdk_results, "next_token", None),
|
|
102
|
+
item_count=len(items),
|
|
103
|
+
field_names=cls.FIELD_NAMES if include_field_names else None,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class DataAssetSearchResults(SearchMeta):
|
|
108
|
+
"""Compact results: {items: [{id, n, d, t}], has_more, next_token, total_count}.
|
|
109
|
+
|
|
110
|
+
Fields: id=id, n=name, d=description (truncated), t=tags (limited).
|
|
111
|
+
Set include_field_names=true to add field_names with full labels.
|
|
112
|
+
Use get_data_asset(id) if full details needed.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
items: list[CompactDataAssetItem]
|
|
116
|
+
FIELD_NAMES: ClassVar[dict[str, str]] = {"id": "id", "n": "name", "d": "description", "t": "tags"}
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def from_sdk_results(cls, sdk_results: Any, include_field_names: bool = False) -> "DataAssetSearchResults":
|
|
120
|
+
"""Convert SDK search results to compact format."""
|
|
121
|
+
items = [
|
|
122
|
+
CompactDataAssetItem(
|
|
123
|
+
id=d.id,
|
|
124
|
+
n=d.name,
|
|
125
|
+
d=truncate_description(d.description),
|
|
126
|
+
t=limit_tags(d.tags),
|
|
127
|
+
)
|
|
128
|
+
for d in sdk_results.results
|
|
129
|
+
]
|
|
130
|
+
return cls(
|
|
131
|
+
items=items,
|
|
132
|
+
has_more=sdk_results.has_more,
|
|
133
|
+
next_token=getattr(sdk_results, "next_token", None),
|
|
134
|
+
item_count=len(items),
|
|
135
|
+
field_names=cls.FIELD_NAMES if include_field_names else None,
|
|
136
|
+
)
|
{codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/server.py
RENAMED
|
@@ -3,6 +3,7 @@ import os
|
|
|
3
3
|
from codeocean import CodeOcean
|
|
4
4
|
from mcp.server.fastmcp import FastMCP
|
|
5
5
|
|
|
6
|
+
from codeocean_mcp_server.logging_config import configure_logging
|
|
6
7
|
from codeocean_mcp_server.tools import (
|
|
7
8
|
capsules,
|
|
8
9
|
computations,
|
|
@@ -13,6 +14,7 @@ from codeocean_mcp_server.tools import (
|
|
|
13
14
|
|
|
14
15
|
def main():
|
|
15
16
|
"""Run the MCP server."""
|
|
17
|
+
configure_logging()
|
|
16
18
|
domain = os.getenv("CODEOCEAN_DOMAIN")
|
|
17
19
|
token = os.getenv("CODEOCEAN_TOKEN")
|
|
18
20
|
if not domain or not token:
|
{codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/tools/capsules.py
RENAMED
|
@@ -3,7 +3,6 @@ from codeocean.capsule import (
|
|
|
3
3
|
AppPanel,
|
|
4
4
|
Capsule,
|
|
5
5
|
CapsuleSearchParams,
|
|
6
|
-
CapsuleSearchResults,
|
|
7
6
|
Computation,
|
|
8
7
|
DataAssetAttachParams,
|
|
9
8
|
DataAssetAttachResults,
|
|
@@ -11,6 +10,7 @@ from codeocean.capsule import (
|
|
|
11
10
|
from mcp.server.fastmcp import FastMCP
|
|
12
11
|
|
|
13
12
|
from codeocean_mcp_server.models import dataclass_to_pydantic
|
|
13
|
+
from codeocean_mcp_server.search import CapsuleSearchResults
|
|
14
14
|
|
|
15
15
|
AppPanelModel = dataclass_to_pydantic(AppPanel)
|
|
16
16
|
CapsuleSearchParamsModel = dataclass_to_pydantic(CapsuleSearchParams)
|
|
@@ -20,31 +20,25 @@ DataAssetAttachParamsModel = dataclass_to_pydantic(DataAssetAttachParams)
|
|
|
20
20
|
def add_tools(mcp: FastMCP, client: CodeOcean):
|
|
21
21
|
"""Add capsule tools to the MCP server."""
|
|
22
22
|
|
|
23
|
-
@mcp.tool(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"like sort_by or sort_order unless requested."
|
|
29
|
-
)
|
|
30
|
-
)
|
|
31
|
-
def search_capsules(search_params: CapsuleSearchParamsModel) -> CapsuleSearchResults:
|
|
23
|
+
@mcp.tool(description=(str(client.capsules.search_capsules.__doc__) + " " + str(CapsuleSearchResults.__doc__)))
|
|
24
|
+
def search_capsules(
|
|
25
|
+
search_params: CapsuleSearchParamsModel,
|
|
26
|
+
include_field_names: bool = False,
|
|
27
|
+
) -> CapsuleSearchResults:
|
|
32
28
|
"""Search for capsules matching specified criteria."""
|
|
33
29
|
params = CapsuleSearchParams(**search_params.model_dump(exclude_none=True))
|
|
34
|
-
|
|
30
|
+
results = client.capsules.search_capsules(params)
|
|
31
|
+
return CapsuleSearchResults.from_sdk_results(results, include_field_names)
|
|
35
32
|
|
|
36
|
-
@mcp.tool(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"like sort_by or sort_order unless requested."
|
|
42
|
-
)
|
|
43
|
-
)
|
|
44
|
-
def search_pipelines(search_params: CapsuleSearchParamsModel) -> CapsuleSearchResults:
|
|
33
|
+
@mcp.tool(description=(str(client.capsules.search_pipelines.__doc__) + " " + str(CapsuleSearchResults.__doc__)))
|
|
34
|
+
def search_pipelines(
|
|
35
|
+
search_params: CapsuleSearchParamsModel,
|
|
36
|
+
include_field_names: bool = False,
|
|
37
|
+
) -> CapsuleSearchResults:
|
|
45
38
|
"""Search for pipelines matching specified criteria."""
|
|
46
39
|
params = CapsuleSearchParams(**search_params.model_dump(exclude_none=True))
|
|
47
|
-
|
|
40
|
+
results = client.capsules.search_pipelines(params)
|
|
41
|
+
return CapsuleSearchResults.from_sdk_results(results, include_field_names)
|
|
48
42
|
|
|
49
43
|
@mcp.tool(
|
|
50
44
|
description=(
|
|
@@ -5,7 +5,6 @@ from codeocean.data_asset import (
|
|
|
5
5
|
DataAsset,
|
|
6
6
|
DataAssetParams,
|
|
7
7
|
DataAssetSearchParams,
|
|
8
|
-
DataAssetSearchResults,
|
|
9
8
|
DataAssetUpdateParams,
|
|
10
9
|
FileURLs,
|
|
11
10
|
Folder,
|
|
@@ -14,6 +13,7 @@ from mcp.server.fastmcp import FastMCP
|
|
|
14
13
|
|
|
15
14
|
from codeocean_mcp_server.file_utils import download_and_read_file
|
|
16
15
|
from codeocean_mcp_server.models import dataclass_to_pydantic
|
|
16
|
+
from codeocean_mcp_server.search import DataAssetSearchResults
|
|
17
17
|
|
|
18
18
|
DataAssetModel = dataclass_to_pydantic(DataAsset)
|
|
19
19
|
DataAssetParamsModel = dataclass_to_pydantic(DataAssetParams)
|
|
@@ -25,17 +25,23 @@ def add_tools(mcp: FastMCP, client: CodeOcean):
|
|
|
25
25
|
"""Add data asset tools to the MCP server."""
|
|
26
26
|
|
|
27
27
|
@mcp.tool(
|
|
28
|
-
description=(
|
|
29
|
-
str(client.data_assets.search_data_assets.__doc__)
|
|
30
|
-
+ "Search for data assets (external or internal). You may filter by "
|
|
31
|
-
"fields such as `origin`, tags, or other criteria supported by the "
|
|
32
|
-
"SDK."
|
|
33
|
-
)
|
|
28
|
+
description=(str(client.data_assets.search_data_assets.__doc__) + " " + str(DataAssetSearchResults.__doc__))
|
|
34
29
|
)
|
|
35
|
-
def search_data_assets(
|
|
30
|
+
def search_data_assets(
|
|
31
|
+
search_params: DataAssetSearchParamsModel,
|
|
32
|
+
include_field_names: bool = False,
|
|
33
|
+
) -> DataAssetSearchResults:
|
|
36
34
|
"""Retrieve data assets matching search criteria for datasets."""
|
|
37
35
|
params = DataAssetSearchParams(**search_params.model_dump(exclude_none=True))
|
|
38
|
-
|
|
36
|
+
results = client.data_assets.search_data_assets(params)
|
|
37
|
+
return DataAssetSearchResults.from_sdk_results(results, include_field_names)
|
|
38
|
+
|
|
39
|
+
@mcp.tool(
|
|
40
|
+
description=("Get full details for a data asset by ID. Use after compact search to retrieve complete metadata.")
|
|
41
|
+
)
|
|
42
|
+
def get_data_asset(data_asset_id: str) -> DataAsset:
|
|
43
|
+
"""Retrieve a data asset by its ID."""
|
|
44
|
+
return client.data_assets.get_data_asset(data_asset_id)
|
|
39
45
|
|
|
40
46
|
@mcp.tool(
|
|
41
47
|
description=(
|
|
@@ -7,7 +7,15 @@ from bedrock_tools_converter import convert_tool_format
|
|
|
7
7
|
|
|
8
8
|
BEDROCK_MODEL = os.getenv("BEDROCK_MODEL") or "amazon.nova-pro-v1:0"
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
_client = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _get_client():
|
|
14
|
+
"""Lazily initialize the Bedrock client."""
|
|
15
|
+
global _client
|
|
16
|
+
if _client is None:
|
|
17
|
+
_client = boto3.client("bedrock-runtime")
|
|
18
|
+
return _client
|
|
11
19
|
|
|
12
20
|
|
|
13
21
|
def call_bedrock(
|
|
@@ -26,4 +34,4 @@ def call_bedrock(
|
|
|
26
34
|
if tools is not None:
|
|
27
35
|
payload["toolConfig"] = convert_tool_format(tools, model)
|
|
28
36
|
|
|
29
|
-
return
|
|
37
|
+
return _get_client().converse(**payload)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Pytest configuration for codeocean-mcp-server tests."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def pytest_addoption(parser):
|
|
7
|
+
"""Add --integration flag to pytest."""
|
|
8
|
+
parser.addoption(
|
|
9
|
+
"--integration",
|
|
10
|
+
action="store_true",
|
|
11
|
+
default=False,
|
|
12
|
+
help="Run integration tests (requires AWS/Bedrock setup)",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def pytest_collection_modifyitems(config, items):
|
|
17
|
+
"""Skip integration tests unless --integration flag is passed."""
|
|
18
|
+
if config.getoption("--integration"):
|
|
19
|
+
return
|
|
20
|
+
skip_integration = pytest.mark.skip(reason="need --integration flag to run")
|
|
21
|
+
for item in items:
|
|
22
|
+
if "integration" in item.keywords:
|
|
23
|
+
item.add_marker(skip_integration)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import pytest
|
|
1
2
|
from bedrock_call import call_bedrock
|
|
2
3
|
from deepdiff import DeepDiff
|
|
3
4
|
from mcp_client import get_tools
|
|
@@ -35,6 +36,7 @@ def check_diff(expected_response: dict, response: dict) -> None:
|
|
|
35
36
|
assert not diff, f"tool_usage diverges from expected:\n\n-----\n{diff!r}\n------"
|
|
36
37
|
|
|
37
38
|
|
|
39
|
+
@pytest.mark.integration
|
|
38
40
|
def test_prompt_generates_correct_tool_usage_sequence(
|
|
39
41
|
agentic_prompt_chain: dict = agentic_prompt_chain,
|
|
40
42
|
):
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""Unit tests for search module."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from codeocean_mcp_server.search import (
|
|
7
|
+
TAGS_TRUNCATION_MARKER,
|
|
8
|
+
TRUNCATION_SUFFIX,
|
|
9
|
+
CapsuleSearchResults,
|
|
10
|
+
DataAssetSearchResults,
|
|
11
|
+
limit_tags,
|
|
12
|
+
truncate_description,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Mock dataclasses for SDK results (private to avoid docstring lint rules)
|
|
17
|
+
@dataclass
|
|
18
|
+
class _MockCapsule:
|
|
19
|
+
id: str
|
|
20
|
+
name: str
|
|
21
|
+
slug: str
|
|
22
|
+
description: Optional[str]
|
|
23
|
+
tags: Optional[list[str]]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class _MockDataAsset:
|
|
28
|
+
id: str
|
|
29
|
+
name: str
|
|
30
|
+
description: Optional[str]
|
|
31
|
+
tags: Optional[list[str]]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class _MockCapsuleResults:
|
|
36
|
+
results: list[_MockCapsule]
|
|
37
|
+
has_more: bool
|
|
38
|
+
next_token: Optional[str] = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class _MockDataAssetResults:
|
|
43
|
+
results: list[_MockDataAsset]
|
|
44
|
+
has_more: bool
|
|
45
|
+
next_token: Optional[str] = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TestTruncateDescription:
|
|
49
|
+
"""Tests for truncate_description function."""
|
|
50
|
+
|
|
51
|
+
def test_none_input(self):
|
|
52
|
+
"""None input returns None."""
|
|
53
|
+
assert truncate_description(None) is None
|
|
54
|
+
|
|
55
|
+
def test_empty_string(self):
|
|
56
|
+
"""Empty string input returns None."""
|
|
57
|
+
assert truncate_description("") is None
|
|
58
|
+
|
|
59
|
+
def test_whitespace_only(self):
|
|
60
|
+
"""Whitespace-only input returns None."""
|
|
61
|
+
assert truncate_description(" ") is None
|
|
62
|
+
|
|
63
|
+
def test_short_description(self):
|
|
64
|
+
"""Under 200 chars returns unchanged text."""
|
|
65
|
+
text = "This is a short description."
|
|
66
|
+
assert truncate_description(text) == text
|
|
67
|
+
|
|
68
|
+
def test_exact_length(self):
|
|
69
|
+
"""Exactly 200 chars returns unchanged."""
|
|
70
|
+
text = "a" * 200
|
|
71
|
+
assert truncate_description(text) == text
|
|
72
|
+
|
|
73
|
+
def test_whitespace_preserved_single(self):
|
|
74
|
+
"""Single newlines and spaces are preserved."""
|
|
75
|
+
text = "Line one\nLine two"
|
|
76
|
+
assert truncate_description(text) == text
|
|
77
|
+
|
|
78
|
+
def test_whitespace_collapsed_multiple(self):
|
|
79
|
+
"""3+ consecutive whitespace chars are collapsed."""
|
|
80
|
+
text = "Word one Word two" # 3 spaces
|
|
81
|
+
assert truncate_description(text) == "Word one Word two"
|
|
82
|
+
|
|
83
|
+
text_newlines = "Line one\n\n\nLine two" # 3 newlines
|
|
84
|
+
assert truncate_description(text_newlines) == "Line one Line two"
|
|
85
|
+
|
|
86
|
+
def test_truncation_at_word_boundary(self):
|
|
87
|
+
"""Truncation occurs at word boundary when possible."""
|
|
88
|
+
# Create text that's over 200 chars with spaces
|
|
89
|
+
text = "word " * 50 # 250 chars
|
|
90
|
+
result = truncate_description(text)
|
|
91
|
+
|
|
92
|
+
assert result is not None
|
|
93
|
+
assert result.endswith(TRUNCATION_SUFFIX)
|
|
94
|
+
assert len(result) <= 200
|
|
95
|
+
# Should end at a word boundary (not mid-word)
|
|
96
|
+
without_suffix = result[: -len(TRUNCATION_SUFFIX)]
|
|
97
|
+
assert without_suffix.endswith("word")
|
|
98
|
+
|
|
99
|
+
def test_truncation_no_space(self):
|
|
100
|
+
"""Long string without spaces hard-cuts."""
|
|
101
|
+
text = "a" * 250
|
|
102
|
+
result = truncate_description(text)
|
|
103
|
+
|
|
104
|
+
assert result is not None
|
|
105
|
+
assert result.endswith(TRUNCATION_SUFFIX)
|
|
106
|
+
assert len(result) == 200
|
|
107
|
+
|
|
108
|
+
def test_truncation_suffix_present(self):
|
|
109
|
+
"""Over 200 chars returns text ending with truncation suffix."""
|
|
110
|
+
text = "This is a long description. " * 20
|
|
111
|
+
result = truncate_description(text)
|
|
112
|
+
|
|
113
|
+
assert result is not None
|
|
114
|
+
assert result.endswith(TRUNCATION_SUFFIX)
|
|
115
|
+
|
|
116
|
+
def test_unicode_handled(self):
|
|
117
|
+
"""Unicode chars handled correctly."""
|
|
118
|
+
text = "Hello \u4e16\u754c! " * 30 # "Hello 世界! " repeated
|
|
119
|
+
result = truncate_description(text)
|
|
120
|
+
|
|
121
|
+
assert result is not None
|
|
122
|
+
# Should be valid UTF-8 string
|
|
123
|
+
result.encode("utf-8")
|
|
124
|
+
|
|
125
|
+
def test_custom_max_length(self):
|
|
126
|
+
"""Custom max_length is respected."""
|
|
127
|
+
text = "a" * 100
|
|
128
|
+
result = truncate_description(text, max_length=50)
|
|
129
|
+
|
|
130
|
+
assert result is not None
|
|
131
|
+
assert len(result) == 50
|
|
132
|
+
assert result.endswith(TRUNCATION_SUFFIX)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class TestLimitTags:
|
|
136
|
+
"""Tests for limit_tags function."""
|
|
137
|
+
|
|
138
|
+
def test_none_input(self):
|
|
139
|
+
"""None returns empty list."""
|
|
140
|
+
assert limit_tags(None) == []
|
|
141
|
+
|
|
142
|
+
def test_empty_list(self):
|
|
143
|
+
"""Empty list returns empty list."""
|
|
144
|
+
assert limit_tags([]) == []
|
|
145
|
+
|
|
146
|
+
def test_under_limit(self):
|
|
147
|
+
"""Under 10 tags returns unchanged."""
|
|
148
|
+
tags = ["tag1", "tag2", "tag3"]
|
|
149
|
+
result = limit_tags(tags)
|
|
150
|
+
assert result == tags
|
|
151
|
+
# Should be a copy, not the same object
|
|
152
|
+
assert result is not tags
|
|
153
|
+
|
|
154
|
+
def test_exact_limit(self):
|
|
155
|
+
"""Exactly 10 tags returns unchanged."""
|
|
156
|
+
tags = [f"tag{i}" for i in range(10)]
|
|
157
|
+
result = limit_tags(tags)
|
|
158
|
+
assert result == tags
|
|
159
|
+
|
|
160
|
+
def test_over_limit(self):
|
|
161
|
+
"""Over 10 tags returns first 9 + marker."""
|
|
162
|
+
tags = [f"tag{i}" for i in range(15)]
|
|
163
|
+
result = limit_tags(tags)
|
|
164
|
+
|
|
165
|
+
assert len(result) == 10
|
|
166
|
+
assert result[:9] == tags[:9]
|
|
167
|
+
assert result[9] == TAGS_TRUNCATION_MARKER
|
|
168
|
+
|
|
169
|
+
def test_custom_max_count(self):
|
|
170
|
+
"""Custom max_count is respected."""
|
|
171
|
+
tags = [f"tag{i}" for i in range(10)]
|
|
172
|
+
result = limit_tags(tags, max_count=5)
|
|
173
|
+
|
|
174
|
+
assert len(result) == 5
|
|
175
|
+
assert result[:4] == tags[:4]
|
|
176
|
+
assert result[4] == TAGS_TRUNCATION_MARKER
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class TestCapsuleSearchResults:
|
|
180
|
+
"""Tests for CapsuleSearchResults model."""
|
|
181
|
+
|
|
182
|
+
def test_from_sdk_results(self):
|
|
183
|
+
"""from_sdk_results converts SDK results correctly."""
|
|
184
|
+
capsules = [
|
|
185
|
+
_MockCapsule(id="1", name="Capsule One", slug="capsule-one", description="First", tags=["a", "b"]),
|
|
186
|
+
_MockCapsule(id="2", name="Capsule Two", slug="capsule-two", description=None, tags=None),
|
|
187
|
+
]
|
|
188
|
+
sdk_results = _MockCapsuleResults(results=capsules, has_more=True, next_token="token123")
|
|
189
|
+
|
|
190
|
+
result = CapsuleSearchResults.from_sdk_results(sdk_results, include_field_names=False)
|
|
191
|
+
|
|
192
|
+
assert len(result.items) == 2
|
|
193
|
+
assert result.items[0].id == "1"
|
|
194
|
+
assert result.items[0].n == "Capsule One"
|
|
195
|
+
assert result.items[0].s == "capsule-one"
|
|
196
|
+
assert result.items[0].d == "First"
|
|
197
|
+
assert result.items[0].t == ["a", "b"]
|
|
198
|
+
assert result.items[1].d is None
|
|
199
|
+
assert result.items[1].t == []
|
|
200
|
+
assert result.has_more is True
|
|
201
|
+
assert result.next_token == "token123"
|
|
202
|
+
assert result.item_count == 2
|
|
203
|
+
assert result.field_names is None
|
|
204
|
+
|
|
205
|
+
def test_from_sdk_results_with_field_names(self):
|
|
206
|
+
"""from_sdk_results includes field_names when requested."""
|
|
207
|
+
sdk_results = _MockCapsuleResults(
|
|
208
|
+
results=[_MockCapsule(id="1", name="Test", slug="test", description=None, tags=None)],
|
|
209
|
+
has_more=False,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
result = CapsuleSearchResults.from_sdk_results(sdk_results, include_field_names=True)
|
|
213
|
+
|
|
214
|
+
assert result.field_names == CapsuleSearchResults.FIELD_NAMES
|
|
215
|
+
|
|
216
|
+
def test_serializable(self):
|
|
217
|
+
"""Result can be serialized to JSON."""
|
|
218
|
+
sdk_results = _MockCapsuleResults(
|
|
219
|
+
results=[_MockCapsule(id="1", name="Test", slug="test", description="Desc", tags=["t"])],
|
|
220
|
+
has_more=False,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
result = CapsuleSearchResults.from_sdk_results(sdk_results)
|
|
224
|
+
json_str = result.model_dump_json()
|
|
225
|
+
|
|
226
|
+
assert "items" in json_str
|
|
227
|
+
assert "has_more" in json_str
|
|
228
|
+
assert "item_count" in json_str
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class TestDataAssetSearchResults:
|
|
232
|
+
"""Tests for DataAssetSearchResults model."""
|
|
233
|
+
|
|
234
|
+
def test_from_sdk_results(self):
|
|
235
|
+
"""from_sdk_results converts SDK results correctly."""
|
|
236
|
+
assets = [
|
|
237
|
+
_MockDataAsset(id="da-1", name="Asset One", description="First asset", tags=["x"]),
|
|
238
|
+
_MockDataAsset(id="da-2", name="Asset Two", description=None, tags=None),
|
|
239
|
+
]
|
|
240
|
+
sdk_results = _MockDataAssetResults(results=assets, has_more=False, next_token=None)
|
|
241
|
+
|
|
242
|
+
result = DataAssetSearchResults.from_sdk_results(sdk_results, include_field_names=False)
|
|
243
|
+
|
|
244
|
+
assert len(result.items) == 2
|
|
245
|
+
assert result.items[0].id == "da-1"
|
|
246
|
+
assert result.items[0].n == "Asset One"
|
|
247
|
+
assert result.items[0].d == "First asset"
|
|
248
|
+
assert result.items[0].t == ["x"]
|
|
249
|
+
assert result.items[1].d is None
|
|
250
|
+
assert result.items[1].t == []
|
|
251
|
+
assert result.has_more is False
|
|
252
|
+
assert result.next_token is None
|
|
253
|
+
assert result.item_count == 2
|
|
254
|
+
assert result.field_names is None
|
|
255
|
+
|
|
256
|
+
def test_truncation_applied(self):
|
|
257
|
+
"""Long descriptions and many tags are truncated."""
|
|
258
|
+
asset = _MockDataAsset(
|
|
259
|
+
id="da-1",
|
|
260
|
+
name="Test",
|
|
261
|
+
description="x" * 300, # Long description
|
|
262
|
+
tags=[f"tag{i}" for i in range(20)], # Many tags
|
|
263
|
+
)
|
|
264
|
+
sdk_results = _MockDataAssetResults(results=[asset], has_more=False)
|
|
265
|
+
|
|
266
|
+
result = DataAssetSearchResults.from_sdk_results(sdk_results)
|
|
267
|
+
|
|
268
|
+
# Description should be truncated
|
|
269
|
+
assert result.items[0].d is not None
|
|
270
|
+
assert result.items[0].d.endswith(TRUNCATION_SUFFIX)
|
|
271
|
+
assert len(result.items[0].d) <= 200
|
|
272
|
+
|
|
273
|
+
# Tags should be limited with marker
|
|
274
|
+
assert len(result.items[0].t) == 10
|
|
275
|
+
assert result.items[0].t[-1] == TAGS_TRUNCATION_MARKER
|
|
@@ -438,6 +438,7 @@ test_cases = [(test["prompt"], test["expected"]) for test in test_response]
|
|
|
438
438
|
ids = [test["id"] for test in test_response]
|
|
439
439
|
|
|
440
440
|
|
|
441
|
+
@pytest.mark.integration
|
|
441
442
|
@pytest.mark.parametrize("prompt,expected_response", test_cases, ids=ids)
|
|
442
443
|
def test_prompt_generating_the_right_tool_usage(prompt: str, expected_response: dict):
|
|
443
444
|
"""Test that the prompt generates the expected tool usage."""
|
|
@@ -323,30 +323,30 @@ wheels = [
|
|
|
323
323
|
|
|
324
324
|
[[package]]
|
|
325
325
|
name = "boto3"
|
|
326
|
-
version = "1.42.
|
|
326
|
+
version = "1.42.17"
|
|
327
327
|
source = { registry = "https://pypi.org/simple" }
|
|
328
328
|
dependencies = [
|
|
329
329
|
{ name = "botocore" },
|
|
330
330
|
{ name = "jmespath" },
|
|
331
331
|
{ name = "s3transfer" },
|
|
332
332
|
]
|
|
333
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
333
|
+
sdist = { url = "https://files.pythonhosted.org/packages/8d/cf/6e4a794e73cbf3e774ec27a5acc8442ce55d97cfc5226a20c1f58d8aee16/boto3-1.42.17.tar.gz", hash = "sha256:8a2e345e96d5ceba755c55539c93f99705f403fbfdeef2e838eabdc56750828b", size = 112776, upload-time = "2025-12-26T20:33:38.289Z" }
|
|
334
334
|
wheels = [
|
|
335
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
335
|
+
{ url = "https://files.pythonhosted.org/packages/6c/53/9f4241152eb1f5a57351232c14ef411ef35fbcc1d97a63873235d0503a8a/boto3-1.42.17-py3-none-any.whl", hash = "sha256:e0ee40f7102712452f6776af891c8f49b5ae9133bdaf22711d6f4a78963c2614", size = 140572, upload-time = "2025-12-26T20:33:36.307Z" },
|
|
336
336
|
]
|
|
337
337
|
|
|
338
338
|
[[package]]
|
|
339
339
|
name = "botocore"
|
|
340
|
-
version = "1.42.
|
|
340
|
+
version = "1.42.17"
|
|
341
341
|
source = { registry = "https://pypi.org/simple" }
|
|
342
342
|
dependencies = [
|
|
343
343
|
{ name = "jmespath" },
|
|
344
344
|
{ name = "python-dateutil" },
|
|
345
345
|
{ name = "urllib3" },
|
|
346
346
|
]
|
|
347
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
347
|
+
sdist = { url = "https://files.pythonhosted.org/packages/4d/d2/128e2d8b9d426bd3a339e7dcf3eedca55f449af121604b6891ae80f6be9a/botocore-1.42.17.tar.gz", hash = "sha256:d73fe22c8e1497e4d59ff7dc68eb05afac68a4a6457656811562285d6132bc04", size = 14911757, upload-time = "2025-12-26T20:33:27.641Z" }
|
|
348
348
|
wheels = [
|
|
349
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
349
|
+
{ url = "https://files.pythonhosted.org/packages/d1/47/cc00f2421f49784de8b9113c94b7b6580db989721f5109a24b9108308fe1/botocore-1.42.17-py3-none-any.whl", hash = "sha256:a832e4c04e63141221480967e9e511363aa54d24c405935fccb913a18583c96b", size = 14586536, upload-time = "2025-12-26T20:33:24.032Z" },
|
|
350
350
|
]
|
|
351
351
|
|
|
352
352
|
[[package]]
|
|
@@ -529,7 +529,7 @@ wheels = [
|
|
|
529
529
|
|
|
530
530
|
[[package]]
|
|
531
531
|
name = "codeocean-mcp-server"
|
|
532
|
-
version = "0.
|
|
532
|
+
version = "0.8.0"
|
|
533
533
|
source = { editable = "." }
|
|
534
534
|
dependencies = [
|
|
535
535
|
{ name = "codeocean" },
|
|
@@ -554,7 +554,7 @@ requires-dist = [
|
|
|
554
554
|
|
|
555
555
|
[package.metadata.requires-dev]
|
|
556
556
|
dev = [
|
|
557
|
-
{ name = "boto3", specifier = ">=1.42.
|
|
557
|
+
{ name = "boto3", specifier = ">=1.42.17" },
|
|
558
558
|
{ name = "deepdiff", specifier = ">=8.6.1" },
|
|
559
559
|
{ name = "hatch", specifier = ">=1.16.2" },
|
|
560
560
|
{ name = "mcp-python-client", specifier = ">=0.1.9" },
|
|
@@ -1184,14 +1184,14 @@ wheels = [
|
|
|
1184
1184
|
|
|
1185
1185
|
[[package]]
|
|
1186
1186
|
name = "jaraco-context"
|
|
1187
|
-
version = "6.0.
|
|
1187
|
+
version = "6.0.2"
|
|
1188
1188
|
source = { registry = "https://pypi.org/simple" }
|
|
1189
1189
|
dependencies = [
|
|
1190
1190
|
{ name = "backports-tarfile", marker = "python_full_version < '3.12'" },
|
|
1191
1191
|
]
|
|
1192
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1192
|
+
sdist = { url = "https://files.pythonhosted.org/packages/8d/7d/41acf8e22d791bde812cb6c2c36128bb932ed8ae066bcb5e39cb198e8253/jaraco_context-6.0.2.tar.gz", hash = "sha256:953ae8dddb57b1d791bf72ea1009b32088840a7dd19b9ba16443f62be919ee57", size = 14994, upload-time = "2025-12-24T19:21:35.784Z" }
|
|
1193
1193
|
wheels = [
|
|
1194
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1194
|
+
{ url = "https://files.pythonhosted.org/packages/c7/0c/1e0096ced9c55f9c6c6655446798df74165780375d3f5ab5f33751e087ae/jaraco_context-6.0.2-py3-none-any.whl", hash = "sha256:55fc21af4b4f9ca94aa643b6ee7fe13b1e4c01abf3aeb98ca4ad9c80b741c786", size = 6988, upload-time = "2025-12-24T19:21:34.557Z" },
|
|
1195
1195
|
]
|
|
1196
1196
|
|
|
1197
1197
|
[[package]]
|
|
@@ -1380,75 +1380,75 @@ wheels = [
|
|
|
1380
1380
|
|
|
1381
1381
|
[[package]]
|
|
1382
1382
|
name = "librt"
|
|
1383
|
-
version = "0.7.
|
|
1384
|
-
source = { registry = "https://pypi.org/simple" }
|
|
1385
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1386
|
-
wheels = [
|
|
1387
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1388
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1389
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1390
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1391
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1392
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1393
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1394
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1395
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1396
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1397
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1398
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1399
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1400
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1401
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1402
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1403
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1404
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1405
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1406
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1407
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1408
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1409
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1410
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1411
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1412
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1413
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1414
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1415
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1416
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1417
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1418
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1419
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1420
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1421
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1422
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1423
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1424
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1425
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1426
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1427
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1428
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1429
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1430
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1431
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1432
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1433
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1434
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1435
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1436
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1437
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1438
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1439
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1440
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1441
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1442
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1443
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1444
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1445
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1446
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1447
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1448
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1449
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1450
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1451
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1383
|
+
version = "0.7.5"
|
|
1384
|
+
source = { registry = "https://pypi.org/simple" }
|
|
1385
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b5/8a/071f6628363d83e803d4783e0cd24fb9c5b798164300fcfaaa47c30659c0/librt-0.7.5.tar.gz", hash = "sha256:de4221a1181fa9c8c4b5f35506ed6f298948f44003d84d2a8b9885d7e01e6cfa", size = 145868, upload-time = "2025-12-25T03:53:16.039Z" }
|
|
1386
|
+
wheels = [
|
|
1387
|
+
{ url = "https://files.pythonhosted.org/packages/88/f2/3248d8419db99ab80bb36266735d1241f766ad5fd993071211f789b618a5/librt-0.7.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81056e01bba1394f1d92904ec61a4078f66df785316275edbaf51d90da8c6e26", size = 54703, upload-time = "2025-12-25T03:51:48.394Z" },
|
|
1388
|
+
{ url = "https://files.pythonhosted.org/packages/7b/30/7e179543dbcb1311f84b7e797658ad85cf2d4474c468f5dbafa13f2a98a5/librt-0.7.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7c72c8756eeb3aefb1b9e3dac7c37a4a25db63640cac0ab6fc18e91a0edf05a", size = 56660, upload-time = "2025-12-25T03:51:49.791Z" },
|
|
1389
|
+
{ url = "https://files.pythonhosted.org/packages/15/91/3ba03ac1ac1abd66757a134b3bd56d9674928b163d0e686ea065a2bbb92d/librt-0.7.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ddc4a16207f88f9597b397fc1f60781266d13b13de922ff61c206547a29e4bbd", size = 161026, upload-time = "2025-12-25T03:51:51.021Z" },
|
|
1390
|
+
{ url = "https://files.pythonhosted.org/packages/0d/6e/b8365f547817d37b44c4be2ffa02630be995ef18be52d72698cecc3640c5/librt-0.7.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63055d3dda433ebb314c9f1819942f16a19203c454508fdb2d167613f7017169", size = 169530, upload-time = "2025-12-25T03:51:52.417Z" },
|
|
1391
|
+
{ url = "https://files.pythonhosted.org/packages/63/6a/8442eb0b6933c651a06e1888f863971f3391cc11338fdaa6ab969f7d1eac/librt-0.7.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f85f9b5db87b0f52e53c68ad2a0c5a53e00afa439bd54a1723742a2b1021276", size = 183272, upload-time = "2025-12-25T03:51:53.713Z" },
|
|
1392
|
+
{ url = "https://files.pythonhosted.org/packages/90/c4/b1166df6ef8e1f68d309f50bf69e8e750a5ea12fe7e2cf202c771ff359fc/librt-0.7.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c566a4672564c5d54d8ab65cdaae5a87ee14c1564c1a2ddc7a9f5811c750f023", size = 179040, upload-time = "2025-12-25T03:51:55.048Z" },
|
|
1393
|
+
{ url = "https://files.pythonhosted.org/packages/fc/30/8f3fd9fd975b16c37832d6c248b976d2a0e33f155063781e064f249b37f1/librt-0.7.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fee15c2a190ef389f14928135c6fb2d25cd3fdb7887bfd9a7b444bbdc8c06b96", size = 173506, upload-time = "2025-12-25T03:51:56.407Z" },
|
|
1394
|
+
{ url = "https://files.pythonhosted.org/packages/75/71/c3d4d5658f9849bf8e07ffba99f892d49a0c9a4001323ed610db72aedc82/librt-0.7.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:584cb3e605ec45ba350962cec853e17be0a25a772f21f09f1e422f7044ae2a7d", size = 193573, upload-time = "2025-12-25T03:51:57.949Z" },
|
|
1395
|
+
{ url = "https://files.pythonhosted.org/packages/86/7c/c1c8a0116a2eed3d58c8946c589a8f9e1354b9b825cc92eba58bb15f6fb1/librt-0.7.5-cp310-cp310-win32.whl", hash = "sha256:9c08527055fbb03c641c15bbc5b79dd2942fb6a3bd8dabf141dd7e97eeea4904", size = 42603, upload-time = "2025-12-25T03:51:59.215Z" },
|
|
1396
|
+
{ url = "https://files.pythonhosted.org/packages/1d/00/b52c77ca294247420020b829b70465c6e6f2b9d59ab21d8051aac20432da/librt-0.7.5-cp310-cp310-win_amd64.whl", hash = "sha256:dd810f2d39c526c42ea205e0addad5dc08ef853c625387806a29d07f9d150d9b", size = 48977, upload-time = "2025-12-25T03:52:00.519Z" },
|
|
1397
|
+
{ url = "https://files.pythonhosted.org/packages/11/89/42b3ccb702a7e5f7a4cf2afc8a0a8f8c5e7d4b4d3a7c3de6357673dddddb/librt-0.7.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f952e1a78c480edee8fb43aa2bf2e84dcd46c917d44f8065b883079d3893e8fc", size = 54705, upload-time = "2025-12-25T03:52:01.433Z" },
|
|
1398
|
+
{ url = "https://files.pythonhosted.org/packages/bb/90/c16970b509c3c448c365041d326eeef5aeb2abaed81eb3187b26a3cd13f8/librt-0.7.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75965c1f4efb7234ff52a58b729d245a21e87e4b6a26a0ec08052f02b16274e4", size = 56667, upload-time = "2025-12-25T03:52:02.391Z" },
|
|
1399
|
+
{ url = "https://files.pythonhosted.org/packages/ac/2f/da4bdf6c190503f4663fbb781dfae5564a2b1c3f39a2da8e1ac7536ac7bd/librt-0.7.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:732e0aa0385b59a1b2545159e781c792cc58ce9c134249233a7c7250a44684c4", size = 161705, upload-time = "2025-12-25T03:52:03.395Z" },
|
|
1400
|
+
{ url = "https://files.pythonhosted.org/packages/fb/88/c5da8e1f5f22b23d56e1fbd87266799dcf32828d47bf69fabc6f9673c6eb/librt-0.7.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cdde31759bd8888f3ef0eebda80394a48961328a17c264dce8cc35f4b9cde35d", size = 171029, upload-time = "2025-12-25T03:52:04.798Z" },
|
|
1401
|
+
{ url = "https://files.pythonhosted.org/packages/38/8a/8dfc00a6f1febc094ed9a55a448fc0b3a591b5dfd83be6cfd76d0910b1f0/librt-0.7.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3146d52465b3b6397d25d513f428cb421c18df65b7378667bb5f1e3cc45805", size = 184704, upload-time = "2025-12-25T03:52:05.887Z" },
|
|
1402
|
+
{ url = "https://files.pythonhosted.org/packages/ad/57/65dec835ff235f431801064a3b41268f2f5ee0d224dc3bbf46d911af5c1a/librt-0.7.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:29c8d2fae11d4379ea207ba7fc69d43237e42cf8a9f90ec6e05993687e6d648b", size = 180720, upload-time = "2025-12-25T03:52:06.925Z" },
|
|
1403
|
+
{ url = "https://files.pythonhosted.org/packages/1e/27/92033d169bbcaa0d9a2dd476c179e5171ec22ed574b1b135a3c6104fb7d4/librt-0.7.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb41f04046b4f22b1e7ba5ef513402cd2e3477ec610e5f92d38fe2bba383d419", size = 174538, upload-time = "2025-12-25T03:52:08.075Z" },
|
|
1404
|
+
{ url = "https://files.pythonhosted.org/packages/44/5c/0127098743575d5340624d8d4ec508d4d5ff0877dcee6f55f54bf03e5ed0/librt-0.7.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8bb7883c1e94ceb87c2bf81385266f032da09cd040e804cc002f2c9d6b842e2f", size = 195240, upload-time = "2025-12-25T03:52:09.427Z" },
|
|
1405
|
+
{ url = "https://files.pythonhosted.org/packages/47/0f/be028c3e906a8ee6d29a42fd362e6d57d4143057f2bc0c454d489a0f898b/librt-0.7.5-cp311-cp311-win32.whl", hash = "sha256:84d4a6b9efd6124f728558a18e79e7cc5c5d4efc09b2b846c910de7e564f5bad", size = 42941, upload-time = "2025-12-25T03:52:10.527Z" },
|
|
1406
|
+
{ url = "https://files.pythonhosted.org/packages/ac/3a/2f0ed57f4c3ae3c841780a95dfbea4cd811c6842d9ee66171ce1af606d25/librt-0.7.5-cp311-cp311-win_amd64.whl", hash = "sha256:ab4b0d3bee6f6ff7017e18e576ac7e41a06697d8dea4b8f3ab9e0c8e1300c409", size = 49244, upload-time = "2025-12-25T03:52:11.832Z" },
|
|
1407
|
+
{ url = "https://files.pythonhosted.org/packages/ee/7c/d7932aedfa5a87771f9e2799e7185ec3a322f4a1f4aa87c234159b75c8c8/librt-0.7.5-cp311-cp311-win_arm64.whl", hash = "sha256:730be847daad773a3c898943cf67fb9845a3961d06fb79672ceb0a8cd8624cfa", size = 42614, upload-time = "2025-12-25T03:52:12.745Z" },
|
|
1408
|
+
{ url = "https://files.pythonhosted.org/packages/33/9d/cb0a296cee177c0fee7999ada1c1af7eee0e2191372058814a4ca6d2baf0/librt-0.7.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba1077c562a046208a2dc6366227b3eeae8f2c2ab4b41eaf4fd2fa28cece4203", size = 55689, upload-time = "2025-12-25T03:52:14.041Z" },
|
|
1409
|
+
{ url = "https://files.pythonhosted.org/packages/79/5c/d7de4d4228b74c5b81a3fbada157754bb29f0e1f8c38229c669a7f90422a/librt-0.7.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:654fdc971c76348a73af5240d8e2529265b9a7ba6321e38dd5bae7b0d4ab3abe", size = 57142, upload-time = "2025-12-25T03:52:15.336Z" },
|
|
1410
|
+
{ url = "https://files.pythonhosted.org/packages/e5/b2/5da779184aae369b69f4ae84225f63741662a0fe422e91616c533895d7a4/librt-0.7.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6b7b58913d475911f6f33e8082f19dd9b120c4f4a5c911d07e395d67b81c6982", size = 165323, upload-time = "2025-12-25T03:52:16.384Z" },
|
|
1411
|
+
{ url = "https://files.pythonhosted.org/packages/5a/40/6d5abc15ab6cc70e04c4d201bb28baffff4cfb46ab950b8e90935b162d58/librt-0.7.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8e0fd344bad57026a8f4ccfaf406486c2fc991838050c2fef156170edc3b775", size = 174218, upload-time = "2025-12-25T03:52:17.518Z" },
|
|
1412
|
+
{ url = "https://files.pythonhosted.org/packages/0d/d0/5239a8507e6117a3cb59ce0095bdd258bd2a93d8d4b819a506da06d8d645/librt-0.7.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46aa91813c267c3f60db75d56419b42c0c0b9748ec2c568a0e3588e543fb4233", size = 189007, upload-time = "2025-12-25T03:52:18.585Z" },
|
|
1413
|
+
{ url = "https://files.pythonhosted.org/packages/1f/a4/8eed1166ffddbb01c25363e4c4e655f4bac298debe9e5a2dcfaf942438a1/librt-0.7.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ddc0ab9dbc5f9ceaf2bf7a367bf01f2697660e908f6534800e88f43590b271db", size = 183962, upload-time = "2025-12-25T03:52:19.723Z" },
|
|
1414
|
+
{ url = "https://files.pythonhosted.org/packages/a1/83/260e60aab2f5ccba04579c5c46eb3b855e51196fde6e2bcf6742d89140a8/librt-0.7.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7a488908a470451338607650f1c064175094aedebf4a4fa37890682e30ce0b57", size = 177611, upload-time = "2025-12-25T03:52:21.18Z" },
|
|
1415
|
+
{ url = "https://files.pythonhosted.org/packages/c4/36/6dcfed0df41e9695665462bab59af15b7ed2b9c668d85c7ebadd022cbb76/librt-0.7.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e47fc52602ffc374e69bf1b76536dc99f7f6dd876bd786c8213eaa3598be030a", size = 199273, upload-time = "2025-12-25T03:52:22.25Z" },
|
|
1416
|
+
{ url = "https://files.pythonhosted.org/packages/a6/b7/157149c8cffae6bc4293a52e0267860cee2398cb270798d94f1c8a69b9ae/librt-0.7.5-cp312-cp312-win32.whl", hash = "sha256:cda8b025875946ffff5a9a7590bf9acde3eb02cb6200f06a2d3e691ef3d9955b", size = 43191, upload-time = "2025-12-25T03:52:23.643Z" },
|
|
1417
|
+
{ url = "https://files.pythonhosted.org/packages/f8/91/197dfeb8d3bdeb0a5344d0d8b3077f183ba5e76c03f158126f6072730998/librt-0.7.5-cp312-cp312-win_amd64.whl", hash = "sha256:b591c094afd0ffda820e931148c9e48dc31a556dc5b2b9b3cc552fa710d858e4", size = 49462, upload-time = "2025-12-25T03:52:24.637Z" },
|
|
1418
|
+
{ url = "https://files.pythonhosted.org/packages/03/ea/052a79454cc52081dfaa9a1c4c10a529f7a6a6805b2fac5805fea5b25975/librt-0.7.5-cp312-cp312-win_arm64.whl", hash = "sha256:532ddc6a8a6ca341b1cd7f4d999043e4c71a212b26fe9fd2e7f1e8bb4e873544", size = 42830, upload-time = "2025-12-25T03:52:25.944Z" },
|
|
1419
|
+
{ url = "https://files.pythonhosted.org/packages/9f/9a/8f61e16de0ff76590af893cfb5b1aa5fa8b13e5e54433d0809c7033f59ed/librt-0.7.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b1795c4b2789b458fa290059062c2f5a297ddb28c31e704d27e161386469691a", size = 55750, upload-time = "2025-12-25T03:52:26.975Z" },
|
|
1420
|
+
{ url = "https://files.pythonhosted.org/packages/05/7c/a8a883804851a066f301e0bad22b462260b965d5c9e7fe3c5de04e6f91f8/librt-0.7.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2fcbf2e135c11f721193aa5f42ba112bb1046afafbffd407cbc81d8d735c74d0", size = 57170, upload-time = "2025-12-25T03:52:27.948Z" },
|
|
1421
|
+
{ url = "https://files.pythonhosted.org/packages/d6/5d/b3b47facf5945be294cf8a835b03589f70ee0e791522f99ec6782ed738b3/librt-0.7.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c039bbf79a9a2498404d1ae7e29a6c175e63678d7a54013a97397c40aee026c5", size = 165834, upload-time = "2025-12-25T03:52:29.09Z" },
|
|
1422
|
+
{ url = "https://files.pythonhosted.org/packages/b4/b6/b26910cd0a4e43e5d02aacaaea0db0d2a52e87660dca08293067ee05601a/librt-0.7.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3919c9407faeeee35430ae135e3a78acd4ecaaaa73767529e2c15ca1d73ba325", size = 174820, upload-time = "2025-12-25T03:52:30.463Z" },
|
|
1423
|
+
{ url = "https://files.pythonhosted.org/packages/a5/a3/81feddd345d4c869b7a693135a462ae275f964fcbbe793d01ea56a84c2ee/librt-0.7.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26b46620e1e0e45af510d9848ea0915e7040605dd2ae94ebefb6c962cbb6f7ec", size = 189609, upload-time = "2025-12-25T03:52:31.492Z" },
|
|
1424
|
+
{ url = "https://files.pythonhosted.org/packages/ce/a9/31310796ef4157d1d37648bf4a3b84555319f14cee3e9bad7bdd7bfd9a35/librt-0.7.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9bbb8facc5375476d392990dd6a71f97e4cb42e2ac66f32e860f6e47299d5e89", size = 184589, upload-time = "2025-12-25T03:52:32.59Z" },
|
|
1425
|
+
{ url = "https://files.pythonhosted.org/packages/32/22/da3900544cb0ac6ab7a2857850158a0a093b86f92b264aa6c4a4f2355ff3/librt-0.7.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e9e9c988b5ffde7be02180f864cbd17c0b0c1231c235748912ab2afa05789c25", size = 178251, upload-time = "2025-12-25T03:52:33.745Z" },
|
|
1426
|
+
{ url = "https://files.pythonhosted.org/packages/db/77/78e02609846e78b9b8c8e361753b3dbac9a07e6d5b567fe518de9e074ab0/librt-0.7.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:edf6b465306215b19dbe6c3fb63cf374a8f3e1ad77f3b4c16544b83033bbb67b", size = 199852, upload-time = "2025-12-25T03:52:34.826Z" },
|
|
1427
|
+
{ url = "https://files.pythonhosted.org/packages/2a/25/05706f6b346429c951582f1b3561f4d5e1418d0d7ba1a0c181237cd77b3b/librt-0.7.5-cp313-cp313-win32.whl", hash = "sha256:060bde69c3604f694bd8ae21a780fe8be46bb3dbb863642e8dfc75c931ca8eee", size = 43250, upload-time = "2025-12-25T03:52:35.905Z" },
|
|
1428
|
+
{ url = "https://files.pythonhosted.org/packages/d9/59/c38677278ac0b9ae1afc611382ef6c9ea87f52ad257bd3d8d65f0eacdc6a/librt-0.7.5-cp313-cp313-win_amd64.whl", hash = "sha256:a82d5a0ee43aeae2116d7292c77cc8038f4841830ade8aa922e098933b468b9e", size = 49421, upload-time = "2025-12-25T03:52:36.895Z" },
|
|
1429
|
+
{ url = "https://files.pythonhosted.org/packages/c0/47/1d71113df4a81de5fdfbd3d7244e05d3d67e89f25455c3380ca50b92741e/librt-0.7.5-cp313-cp313-win_arm64.whl", hash = "sha256:3c98a8d0ac9e2a7cb8ff8c53e5d6e8d82bfb2839abf144fdeaaa832f2a12aa45", size = 42827, upload-time = "2025-12-25T03:52:37.856Z" },
|
|
1430
|
+
{ url = "https://files.pythonhosted.org/packages/97/ae/8635b4efdc784220f1378be640d8b1a794332f7f6ea81bb4859bf9d18aa7/librt-0.7.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9937574e6d842f359b8585903d04f5b4ab62277a091a93e02058158074dc52f2", size = 55191, upload-time = "2025-12-25T03:52:38.839Z" },
|
|
1431
|
+
{ url = "https://files.pythonhosted.org/packages/52/11/ed7ef6955dc2032af37db9b0b31cd5486a138aa792e1bb9e64f0f4950e27/librt-0.7.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5cd3afd71e9bc146203b6c8141921e738364158d4aa7cdb9a874e2505163770f", size = 56894, upload-time = "2025-12-25T03:52:39.805Z" },
|
|
1432
|
+
{ url = "https://files.pythonhosted.org/packages/24/f1/02921d4a66a1b5dcd0493b89ce76e2762b98c459fe2ad04b67b2ea6fdd39/librt-0.7.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9cffa3ef0af29687455161cb446eff059bf27607f95163d6a37e27bcb37180f6", size = 163726, upload-time = "2025-12-25T03:52:40.79Z" },
|
|
1433
|
+
{ url = "https://files.pythonhosted.org/packages/65/87/27df46d2756fcb7a82fa7f6ca038a0c6064c3e93ba65b0b86fbf6a4f76a2/librt-0.7.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82f3f088482e2229387eadf8215c03f7726d56f69cce8c0c40f0795aebc9b361", size = 172470, upload-time = "2025-12-25T03:52:42.226Z" },
|
|
1434
|
+
{ url = "https://files.pythonhosted.org/packages/9f/a9/e65a35e5d423639f4f3d8e17301ff13cc41c2ff97677fe9c361c26dbfbb7/librt-0.7.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7aa33153a5bb0bac783d2c57885889b1162823384e8313d47800a0e10d0070e", size = 186807, upload-time = "2025-12-25T03:52:43.688Z" },
|
|
1435
|
+
{ url = "https://files.pythonhosted.org/packages/d7/b0/ac68aa582a996b1241773bd419823290c42a13dc9f494704a12a17ddd7b6/librt-0.7.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:265729b551a2dd329cc47b323a182fb7961af42abf21e913c9dd7d3331b2f3c2", size = 181810, upload-time = "2025-12-25T03:52:45.095Z" },
|
|
1436
|
+
{ url = "https://files.pythonhosted.org/packages/e1/c1/03f6717677f20acd2d690813ec2bbe12a2de305f32c61479c53f7b9413bc/librt-0.7.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:168e04663e126416ba712114050f413ac306759a1791d87b7c11d4428ba75760", size = 175599, upload-time = "2025-12-25T03:52:46.177Z" },
|
|
1437
|
+
{ url = "https://files.pythonhosted.org/packages/01/d7/f976ff4c07c59b69bb5eec7e5886d43243075bbef834428124b073471c86/librt-0.7.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:553dc58987d1d853adda8aeadf4db8e29749f0b11877afcc429a9ad892818ae2", size = 196506, upload-time = "2025-12-25T03:52:47.327Z" },
|
|
1438
|
+
{ url = "https://files.pythonhosted.org/packages/b7/74/004f068b8888e61b454568b5479f88018fceb14e511ac0609cccee7dd227/librt-0.7.5-cp314-cp314-win32.whl", hash = "sha256:263f4fae9eba277513357c871275b18d14de93fd49bf5e43dc60a97b81ad5eb8", size = 39747, upload-time = "2025-12-25T03:52:48.437Z" },
|
|
1439
|
+
{ url = "https://files.pythonhosted.org/packages/37/b1/ea3ec8fcf5f0a00df21f08972af77ad799604a306db58587308067d27af8/librt-0.7.5-cp314-cp314-win_amd64.whl", hash = "sha256:85f485b7471571e99fab4f44eeb327dc0e1f814ada575f3fa85e698417d8a54e", size = 45970, upload-time = "2025-12-25T03:52:49.389Z" },
|
|
1440
|
+
{ url = "https://files.pythonhosted.org/packages/5d/30/5e3fb7ac4614a50fc67e6954926137d50ebc27f36419c9963a94f931f649/librt-0.7.5-cp314-cp314-win_arm64.whl", hash = "sha256:49c596cd18e90e58b7caa4d7ca7606049c1802125fcff96b8af73fa5c3870e4d", size = 39075, upload-time = "2025-12-25T03:52:50.395Z" },
|
|
1441
|
+
{ url = "https://files.pythonhosted.org/packages/a4/7f/0af0a9306a06c2aabee3a790f5aa560c50ec0a486ab818a572dd3db6c851/librt-0.7.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:54d2aef0b0f5056f130981ad45081b278602ff3657fe16c88529f5058038e802", size = 57375, upload-time = "2025-12-25T03:52:51.439Z" },
|
|
1442
|
+
{ url = "https://files.pythonhosted.org/packages/57/1f/c85e510baf6572a3d6ef40c742eacedc02973ed2acdb5dba2658751d9af8/librt-0.7.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0b4791202296ad51ac09a3ff58eb49d9da8e3a4009167a6d76ac418a974e5fd4", size = 59234, upload-time = "2025-12-25T03:52:52.687Z" },
|
|
1443
|
+
{ url = "https://files.pythonhosted.org/packages/49/b1/bb6535e4250cd18b88d6b18257575a0239fa1609ebba925f55f51ae08e8e/librt-0.7.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e860909fea75baef941ee6436e0453612505883b9d0d87924d4fda27865b9a2", size = 183873, upload-time = "2025-12-25T03:52:53.705Z" },
|
|
1444
|
+
{ url = "https://files.pythonhosted.org/packages/8e/49/ad4a138cca46cdaa7f0e15fa912ce3ccb4cc0d4090bfeb8ccc35766fa6d5/librt-0.7.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f02c4337bf271c4f06637f5ff254fad2238c0b8e32a3a480ebb2fc5e26f754a5", size = 194609, upload-time = "2025-12-25T03:52:54.884Z" },
|
|
1445
|
+
{ url = "https://files.pythonhosted.org/packages/9c/2d/3b3cb933092d94bb2c1d3c9b503d8775f08d806588c19a91ee4d1495c2a8/librt-0.7.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7f51ffe59f4556243d3cc82d827bde74765f594fa3ceb80ec4de0c13ccd3416", size = 206777, upload-time = "2025-12-25T03:52:55.969Z" },
|
|
1446
|
+
{ url = "https://files.pythonhosted.org/packages/3a/52/6e7611d3d1347812233dabc44abca4c8065ee97b83c9790d7ecc3f782bc8/librt-0.7.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0b7f080ba30601dfa3e3deed3160352273e1b9bc92e652f51103c3e9298f7899", size = 203208, upload-time = "2025-12-25T03:52:57.036Z" },
|
|
1447
|
+
{ url = "https://files.pythonhosted.org/packages/27/aa/466ae4654bd2d45903fbf180815d41e3ae8903e5a1861f319f73c960a843/librt-0.7.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fb565b4219abc8ea2402e61c7ba648a62903831059ed3564fa1245cc245d58d7", size = 196698, upload-time = "2025-12-25T03:52:58.481Z" },
|
|
1448
|
+
{ url = "https://files.pythonhosted.org/packages/97/8f/424f7e4525bb26fe0d3e984d1c0810ced95e53be4fd867ad5916776e18a3/librt-0.7.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a3cfb15961e7333ea6ef033dc574af75153b5c230d5ad25fbcd55198f21e0cf", size = 217194, upload-time = "2025-12-25T03:52:59.575Z" },
|
|
1449
|
+
{ url = "https://files.pythonhosted.org/packages/9e/33/13a4cb798a171b173f3c94db23adaf13a417130e1493933dc0df0d7fb439/librt-0.7.5-cp314-cp314t-win32.whl", hash = "sha256:118716de5ad6726332db1801bc90fa6d94194cd2e07c1a7822cebf12c496714d", size = 40282, upload-time = "2025-12-25T03:53:01.091Z" },
|
|
1450
|
+
{ url = "https://files.pythonhosted.org/packages/5f/f1/62b136301796399d65dad73b580f4509bcbd347dff885a450bff08e80cb6/librt-0.7.5-cp314-cp314t-win_amd64.whl", hash = "sha256:3dd58f7ce20360c6ce0c04f7bd9081c7f9c19fc6129a3c705d0c5a35439f201d", size = 46764, upload-time = "2025-12-25T03:53:02.381Z" },
|
|
1451
|
+
{ url = "https://files.pythonhosted.org/packages/49/cb/940431d9410fda74f941f5cd7f0e5a22c63be7b0c10fa98b2b7022b48cb1/librt-0.7.5-cp314-cp314t-win_arm64.whl", hash = "sha256:08153ea537609d11f774d2bfe84af39d50d5c9ca3a4d061d946e0c9d8bce04a1", size = 39728, upload-time = "2025-12-25T03:53:03.306Z" },
|
|
1452
1452
|
]
|
|
1453
1453
|
|
|
1454
1454
|
[[package]]
|
|
@@ -2744,15 +2744,15 @@ wheels = [
|
|
|
2744
2744
|
|
|
2745
2745
|
[[package]]
|
|
2746
2746
|
name = "sse-starlette"
|
|
2747
|
-
version = "3.
|
|
2747
|
+
version = "3.1.1"
|
|
2748
2748
|
source = { registry = "https://pypi.org/simple" }
|
|
2749
2749
|
dependencies = [
|
|
2750
2750
|
{ name = "anyio" },
|
|
2751
2751
|
{ name = "starlette" },
|
|
2752
2752
|
]
|
|
2753
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
2753
|
+
sdist = { url = "https://files.pythonhosted.org/packages/62/08/8f554b0e5bad3e4e880521a1686d96c05198471eed860b0eb89b57ea3636/sse_starlette-3.1.1.tar.gz", hash = "sha256:bffa531420c1793ab224f63648c059bcadc412bf9fdb1301ac8de1cf9a67b7fb", size = 24306, upload-time = "2025-12-26T15:22:53.836Z" }
|
|
2754
2754
|
wheels = [
|
|
2755
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
2755
|
+
{ url = "https://files.pythonhosted.org/packages/e3/31/4c281581a0f8de137b710a07f65518b34bcf333b201cfa06cfda9af05f8a/sse_starlette-3.1.1-py3-none-any.whl", hash = "sha256:bb38f71ae74cfd86b529907a9fda5632195dfa6ae120f214ea4c890c7ee9d436", size = 12442, upload-time = "2025-12-26T15:22:52.911Z" },
|
|
2756
2756
|
]
|
|
2757
2757
|
|
|
2758
2758
|
[[package]]
|
|
@@ -2944,15 +2944,15 @@ wheels = [
|
|
|
2944
2944
|
|
|
2945
2945
|
[[package]]
|
|
2946
2946
|
name = "typer-slim"
|
|
2947
|
-
version = "0.
|
|
2947
|
+
version = "0.21.0"
|
|
2948
2948
|
source = { registry = "https://pypi.org/simple" }
|
|
2949
2949
|
dependencies = [
|
|
2950
2950
|
{ name = "click" },
|
|
2951
2951
|
{ name = "typing-extensions" },
|
|
2952
2952
|
]
|
|
2953
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
2953
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f9/3b/2f60ce16f578b1db5b8816d37d6a4d9786b33b76407fc8c13b0b86312c31/typer_slim-0.21.0.tar.gz", hash = "sha256:f2dbd150cfa0fead2242e21fa9f654dfc64773763ddf07c6be9a49ad34f79557", size = 106841, upload-time = "2025-12-25T09:54:55.998Z" }
|
|
2954
2954
|
wheels = [
|
|
2955
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
2955
|
+
{ url = "https://files.pythonhosted.org/packages/b4/84/e97abf10e4a699194ff07fd586ec7f4cf867d9d04bead559a65f9e7aff84/typer_slim-0.21.0-py3-none-any.whl", hash = "sha256:92aee2188ac6fc2b2924bd75bb61a340b78bd8cd51fd9735533ce5a856812c8e", size = 47174, upload-time = "2025-12-25T09:54:54.609Z" },
|
|
2956
2956
|
]
|
|
2957
2957
|
|
|
2958
2958
|
[[package]]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/__init__.py
RENAMED
|
File without changes
|
{codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/file_utils.py
RENAMED
|
File without changes
|
{codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/models.py
RENAMED
|
File without changes
|
{codeocean_mcp_server-0.7.1 → codeocean_mcp_server-0.8.0}/src/codeocean_mcp_server/tools/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|