sequenx-mcp 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sequenx_mcp/__init__.py +6 -0
- sequenx_mcp/__main__.py +6 -0
- sequenx_mcp/_version.py +1 -0
- sequenx_mcp/errors.py +71 -0
- sequenx_mcp/server.py +115 -0
- sequenx_mcp/tools.py +97 -0
- sequenx_mcp-0.1.0.dist-info/METADATA +112 -0
- sequenx_mcp-0.1.0.dist-info/RECORD +10 -0
- sequenx_mcp-0.1.0.dist-info/WHEEL +5 -0
- sequenx_mcp-0.1.0.dist-info/top_level.txt +1 -0
sequenx_mcp/__init__.py
ADDED
sequenx_mcp/__main__.py
ADDED
sequenx_mcp/_version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
sequenx_mcp/errors.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Map the SDK's typed errors onto the contract's error shape.
|
|
2
|
+
|
|
3
|
+
The contract answers with ``ErrorResponse`` = ``{"error": {code, message,
|
|
4
|
+
request_id, details?}}`` and does not use RFC 7807 (see
|
|
5
|
+
``docs/plans/phase_3/06-MCP服务器与宿主Agent工具面.md`` §3.2). A tool therefore
|
|
6
|
+
reports the API's own error body as the tool error message, so the host agent
|
|
7
|
+
sees a structured error instead of a silent empty answer. The MCP framework
|
|
8
|
+
prefixes the message with its own text ("Error executing tool …"), so the
|
|
9
|
+
envelope travels inside the message rather than as the whole message.
|
|
10
|
+
|
|
11
|
+
The body keeps the contract's ``ErrorDetail`` fields — ``code``, ``message``,
|
|
12
|
+
``request_id``, ``category``, ``retryable`` and ``details`` — and adds the
|
|
13
|
+
retry hints the RFC asks the host to see (``retry_after`` and ``limit_type``)
|
|
14
|
+
inside ``details``. ``category`` and ``retryable`` are only present when the
|
|
15
|
+
upstream body carried them.
|
|
16
|
+
|
|
17
|
+
Three codes are the SDK's own, because no contract code covers them: the
|
|
18
|
+
contract's list (``x-error-codes`` in openapi.yaml) describes server-side
|
|
19
|
+
failures only. ``HTTP_<status>`` is a response without a code, ``REQUEST_FAILED``
|
|
20
|
+
a response the SDK could not read, ``TRANSPORT_ERROR`` a request that never
|
|
21
|
+
reached the server.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
from typing import Any
|
|
28
|
+
|
|
29
|
+
from mcp.server.mcpserver.exceptions import ToolError
|
|
30
|
+
from sequenx import RateLimitedError, SequenxError, TransportError
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def error_body(exc: SequenxError) -> dict[str, Any]:
|
|
34
|
+
"""The contract's ErrorDetail body for an SDK error."""
|
|
35
|
+
details: dict[str, Any] = {}
|
|
36
|
+
if isinstance(exc.details, dict):
|
|
37
|
+
details.update(exc.details)
|
|
38
|
+
elif exc.details is not None:
|
|
39
|
+
details["details"] = exc.details
|
|
40
|
+
if isinstance(exc, RateLimitedError):
|
|
41
|
+
if exc.retry_after is not None:
|
|
42
|
+
details["retry_after"] = exc.retry_after
|
|
43
|
+
if exc.limit_type is not None:
|
|
44
|
+
details["limit_type"] = exc.limit_type
|
|
45
|
+
if exc.code:
|
|
46
|
+
code = exc.code
|
|
47
|
+
elif exc.status is not None:
|
|
48
|
+
code = f"HTTP_{exc.status}"
|
|
49
|
+
elif isinstance(exc, TransportError):
|
|
50
|
+
# No response reached us at all.
|
|
51
|
+
code = "TRANSPORT_ERROR"
|
|
52
|
+
else:
|
|
53
|
+
# A client-side failure (e.g. a response without a JSON body).
|
|
54
|
+
code = "REQUEST_FAILED"
|
|
55
|
+
body: dict[str, Any] = {
|
|
56
|
+
"code": code,
|
|
57
|
+
"message": str(exc),
|
|
58
|
+
"request_id": exc.request_id,
|
|
59
|
+
}
|
|
60
|
+
if exc.category is not None:
|
|
61
|
+
body["category"] = exc.category
|
|
62
|
+
if exc.retryable is not None:
|
|
63
|
+
body["retryable"] = exc.retryable
|
|
64
|
+
if details:
|
|
65
|
+
body["details"] = details
|
|
66
|
+
return {"error": body}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def tool_error(exc: SequenxError) -> ToolError:
|
|
70
|
+
"""A tool error carrying the contract's error body as its message."""
|
|
71
|
+
return ToolError(json.dumps(error_body(exc), ensure_ascii=False))
|
sequenx_mcp/server.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""The MCP server: three tools over the SequenxAI public API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Annotated, Any
|
|
9
|
+
|
|
10
|
+
from mcp.server.mcpserver import MCPServer
|
|
11
|
+
from pydantic import Field
|
|
12
|
+
from sequenx import SequenxClient
|
|
13
|
+
|
|
14
|
+
from . import tools
|
|
15
|
+
from ._version import __version__
|
|
16
|
+
from .tools import Point
|
|
17
|
+
|
|
18
|
+
INSTRUCTIONS = (
|
|
19
|
+
"Time-series forecasting on the SequenxAI platform. The data is passed inline as "
|
|
20
|
+
"long-table rows (unique_id, ds, y). Start with list_models to learn the models and "
|
|
21
|
+
"their limits, clean_data to preview how the data will be cleaned (free), and "
|
|
22
|
+
"forecast to get the prediction and the usage that was billed."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
LIST_MODELS_DESCRIPTION = (
|
|
26
|
+
"List the public time-series models available to this API key: each model's id, the "
|
|
27
|
+
"default model, its context and horizon limits and its capabilities. Call it before "
|
|
28
|
+
"forecasting to learn the limits; the catalog is owned by the server and changes "
|
|
29
|
+
"without a version bump."
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
CLEAN_DATA_DESCRIPTION = (
|
|
33
|
+
"Run the free cleaning dry run: the same cleaning stage as a forecast, returning the "
|
|
34
|
+
"cleaning report (with every automatic decision) and the effective policy. No "
|
|
35
|
+
"inference, no billing. Input is inline rows (unique_id, ds, y); freq, model, "
|
|
36
|
+
"clean_series and options are the contract's CleanRequest fields."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
FORECAST_DESCRIPTION = (
|
|
40
|
+
"Forecast one or more series from inline rows (unique_id, ds, y): point forecast plus "
|
|
41
|
+
"intervals, the data-quality report and the billed usage. The tool takes the request "
|
|
42
|
+
"fields h, model, freq, level and options; the remaining contract fields (quantile "
|
|
43
|
+
"policy, covariates, joint groups, ...) are available through REST or the SDK. Two "
|
|
44
|
+
"limits to know: the model reads only the most recent points of a series, so a longer "
|
|
45
|
+
"history is truncated by the API and reported (model_input[].truncated plus a "
|
|
46
|
+
"context_truncated warning) — list_models gives each model's context window; and a "
|
|
47
|
+
"host tool call cannot carry a large table, so for larger data use the REST or SDK "
|
|
48
|
+
"batch path: POST /v1/forecast/batch, then poll GET /v1/forecast/batch/{job_id}."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# The contract's horizon bounds (ForecastRequest.h: 1..2048).
|
|
52
|
+
Horizon = Annotated[int, Field(ge=1, le=2048)]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def build_server(client: SequenxClient) -> MCPServer:
|
|
56
|
+
"""The MCP server with the three tools, bound to one API client."""
|
|
57
|
+
server = MCPServer(name="sequenx", version=__version__, instructions=INSTRUCTIONS)
|
|
58
|
+
|
|
59
|
+
@server.tool(name="list_models", description=LIST_MODELS_DESCRIPTION)
|
|
60
|
+
def list_models_tool() -> dict[str, Any]:
|
|
61
|
+
return tools.list_models(client)
|
|
62
|
+
|
|
63
|
+
@server.tool(name="clean_data", description=CLEAN_DATA_DESCRIPTION)
|
|
64
|
+
def clean_data_tool(
|
|
65
|
+
series: list[Point],
|
|
66
|
+
options: dict[str, Any] | None = None,
|
|
67
|
+
freq: str | None = None,
|
|
68
|
+
model: str | None = None,
|
|
69
|
+
clean_series: bool | None = None,
|
|
70
|
+
) -> dict[str, Any]:
|
|
71
|
+
return tools.clean_data(
|
|
72
|
+
client, series, options=options, freq=freq, model=model, clean_series=clean_series
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
@server.tool(name="forecast", description=FORECAST_DESCRIPTION)
|
|
76
|
+
def forecast_tool(
|
|
77
|
+
series: list[Point],
|
|
78
|
+
h: Horizon,
|
|
79
|
+
model: str | None = None,
|
|
80
|
+
freq: str | None = None,
|
|
81
|
+
level: list[int] | None = None,
|
|
82
|
+
options: dict[str, Any] | None = None,
|
|
83
|
+
) -> dict[str, Any]:
|
|
84
|
+
return tools.forecast(
|
|
85
|
+
client, series, h=h, model=model, freq=freq, level=level, options=options
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return server
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _quiet_httpx() -> None:
|
|
92
|
+
"""The MCP SDK configures the root logger, so httpx's per-request INFO lines would
|
|
93
|
+
reach the host's stderr on every tool call. The protocol lives on stdout; these lines
|
|
94
|
+
are noise there."""
|
|
95
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
96
|
+
logging.getLogger("httpcore").setLevel(logging.WARNING)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def main() -> int:
|
|
100
|
+
"""Run the server over stdio (the transport a local MCP host uses)."""
|
|
101
|
+
api_key = os.environ.get("SEQUENX_API_KEY")
|
|
102
|
+
if not api_key:
|
|
103
|
+
print(
|
|
104
|
+
"set SEQUENX_API_KEY=<sqx_live_... key> to run the SequenxAI MCP server",
|
|
105
|
+
file=sys.stderr,
|
|
106
|
+
)
|
|
107
|
+
return 2
|
|
108
|
+
base_url = os.environ.get("SEQUENX_API_BASE_URL") or SequenxClient.DEFAULT_BASE_URL
|
|
109
|
+
_quiet_httpx()
|
|
110
|
+
client = SequenxClient(api_key, base_url=base_url)
|
|
111
|
+
try:
|
|
112
|
+
build_server(client).run()
|
|
113
|
+
finally:
|
|
114
|
+
client.close()
|
|
115
|
+
return 0
|
sequenx_mcp/tools.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""The three tools: thin adapters over the public API.
|
|
2
|
+
|
|
3
|
+
Each tool maps one-to-one onto an existing operation (RFC
|
|
4
|
+
``docs/plans/phase_3/06-MCP服务器与宿主Agent工具面.md`` §2) and calls the Python
|
|
5
|
+
SDK — there is no second HTTP client and no business logic here. The server
|
|
6
|
+
adds no limits of its own: the control plane owns the point caps and the model
|
|
7
|
+
context, and it reports what it did (``model_input[].truncated`` plus a
|
|
8
|
+
``context_truncated`` warning).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from pydantic import BaseModel, ConfigDict
|
|
16
|
+
from sequenx import SequenxClient, SequenxError
|
|
17
|
+
|
|
18
|
+
from .errors import tool_error
|
|
19
|
+
|
|
20
|
+
# tool name -> (the SDK method it calls, the contract path, the contract method)
|
|
21
|
+
TOOL_OPERATIONS = {
|
|
22
|
+
"list_models": ("models", "/models", "get"),
|
|
23
|
+
"clean_data": ("clean", "/clean", "post"),
|
|
24
|
+
"forecast": ("forecast", "/forecast", "post"),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Point(BaseModel):
|
|
29
|
+
"""One row of the contract's long table (contract: TimeSeriesPoint)."""
|
|
30
|
+
|
|
31
|
+
model_config = ConfigDict(extra="allow")
|
|
32
|
+
|
|
33
|
+
unique_id: str
|
|
34
|
+
ds: str
|
|
35
|
+
y: float | None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def rows_of(series: list[Point]) -> list[dict[str, Any]]:
|
|
39
|
+
"""The rows as the API takes them (the contract's long table)."""
|
|
40
|
+
return [point.model_dump() for point in series]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def list_models(client: SequenxClient) -> dict[str, Any]:
|
|
44
|
+
"""GET /models: the public model catalog for this key."""
|
|
45
|
+
try:
|
|
46
|
+
return dict(client.models())
|
|
47
|
+
except SequenxError as exc:
|
|
48
|
+
raise tool_error(exc) from exc
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def clean_data(
|
|
52
|
+
client: SequenxClient,
|
|
53
|
+
series: list[Point],
|
|
54
|
+
options: dict[str, Any] | None = None,
|
|
55
|
+
freq: str | None = None,
|
|
56
|
+
model: str | None = None,
|
|
57
|
+
clean_series: bool | None = None,
|
|
58
|
+
) -> dict[str, Any]:
|
|
59
|
+
"""POST /clean: the free cleaning dry-run (report + effective policy)."""
|
|
60
|
+
extra: dict[str, Any] = {}
|
|
61
|
+
if options is not None:
|
|
62
|
+
extra["options"] = options
|
|
63
|
+
if freq is not None:
|
|
64
|
+
extra["freq"] = freq
|
|
65
|
+
if model is not None:
|
|
66
|
+
extra["model"] = model
|
|
67
|
+
if clean_series is not None:
|
|
68
|
+
extra["clean_series"] = clean_series
|
|
69
|
+
try:
|
|
70
|
+
return dict(client.clean(rows_of(series), **extra))
|
|
71
|
+
except SequenxError as exc:
|
|
72
|
+
raise tool_error(exc) from exc
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def forecast(
|
|
76
|
+
client: SequenxClient,
|
|
77
|
+
series: list[Point],
|
|
78
|
+
h: int,
|
|
79
|
+
model: str | None = None,
|
|
80
|
+
freq: str | None = None,
|
|
81
|
+
level: list[int] | None = None,
|
|
82
|
+
options: dict[str, Any] | None = None,
|
|
83
|
+
) -> dict[str, Any]:
|
|
84
|
+
"""POST /forecast: a synchronous forecast over inline rows."""
|
|
85
|
+
extra: dict[str, Any] = {}
|
|
86
|
+
if model is not None:
|
|
87
|
+
extra["model"] = model
|
|
88
|
+
if freq is not None:
|
|
89
|
+
extra["freq"] = freq
|
|
90
|
+
if level is not None:
|
|
91
|
+
extra["level"] = level
|
|
92
|
+
if options is not None:
|
|
93
|
+
extra["options"] = options
|
|
94
|
+
try:
|
|
95
|
+
return dict(client.forecast(rows_of(series), h=h, **extra))
|
|
96
|
+
except SequenxError as exc:
|
|
97
|
+
raise tool_error(exc) from exc
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sequenx-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server exposing the SequenxAI public API as tools for AI hosts
|
|
5
|
+
Author-email: SequenxAI <lcw_1497007939@163.com>
|
|
6
|
+
License-Expression: LicenseRef-Proprietary
|
|
7
|
+
Keywords: mcp,time-series,forecasting,sequenxai
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: mcp<3,>=2.3
|
|
17
|
+
Requires-Dist: pydantic<3,>=2.7
|
|
18
|
+
Requires-Dist: sequenx-ai>=0.1.0
|
|
19
|
+
|
|
20
|
+
# sequenx-mcp (SequenxAI MCP server)
|
|
21
|
+
|
|
22
|
+
MCP server that exposes the SequenxAI public API as tools for AI hosts (Claude
|
|
23
|
+
Desktop, Cursor, …). It is a thin adapter: every tool maps one-to-one onto an
|
|
24
|
+
existing operation and calls the Python SDK — there is no second HTTP client and
|
|
25
|
+
no business logic here (see
|
|
26
|
+
[`docs/plans/phase_3/06-MCP服务器与宿主Agent工具面.md`](../../docs/plans/phase_3/06-MCP服务器与宿主Agent工具面.md)).
|
|
27
|
+
|
|
28
|
+
Not published on PyPI yet; it lives in this repository.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
The server depends on the sibling SDK, which is not on PyPI either — install it
|
|
33
|
+
from this repository first:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install -e packages/sequenx-py
|
|
37
|
+
pip install -e packages/sequenx-mcp
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Run
|
|
41
|
+
|
|
42
|
+
The server speaks stdio, the transport a local MCP host starts as a child
|
|
43
|
+
process:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
SEQUENX_API_KEY=sqx_live_... python -m sequenx_mcp
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
| Variable | Meaning |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `SEQUENX_API_KEY` | The `sqx_live_*` key the tools call with (required). |
|
|
52
|
+
| `SEQUENX_API_BASE_URL` | Base URL override; default `https://api.sequenxai.com/v1`. |
|
|
53
|
+
|
|
54
|
+
Host configuration (any MCP host that starts stdio servers):
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"sequenx": {
|
|
60
|
+
"command": "python",
|
|
61
|
+
"args": ["-m", "sequenx_mcp"],
|
|
62
|
+
"env": { "SEQUENX_API_KEY": "sqx_live_..." }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Tools
|
|
69
|
+
|
|
70
|
+
| Tool | Endpoint | What it does |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `list_models` | `GET /v1/models` | The public model catalog for the key: ids, the default model, per-model context / horizon limits and capabilities. |
|
|
73
|
+
| `clean_data` | `POST /v1/clean` | The free cleaning dry-run: cleaning report and effective policy. No inference, no billing. Takes `series`, `options`, `freq`, `model` and `clean_series`. |
|
|
74
|
+
| `forecast` | `POST /v1/forecast` | Synchronous forecast over inline rows: points and intervals, data-quality report, billed usage. Takes `series`, `h`, `model`, `freq`, `level` and `options`. |
|
|
75
|
+
|
|
76
|
+
Input rows are the contract's long table: `unique_id`, `ds`, `y`.
|
|
77
|
+
|
|
78
|
+
**Limits.** `forecast` takes inline rows only — the public API has no dataset
|
|
79
|
+
upload, so the tool is for small-to-medium series. The model reads only the most
|
|
80
|
+
recent points of a series: a longer history is truncated by the API and
|
|
81
|
+
reported (`model_input[].truncated` plus a `context_truncated` warning), and
|
|
82
|
+
`list_models` gives each model's context window. The server adds no limit of
|
|
83
|
+
its own. For larger data use the REST or SDK batch path:
|
|
84
|
+
`POST /v1/forecast/batch`, then poll `GET /v1/forecast/batch/{job_id}`.
|
|
85
|
+
|
|
86
|
+
**Errors.** A tool error carries the contract's error body
|
|
87
|
+
(`{"error": {code, message, request_id, category, retryable, details?}}`), so
|
|
88
|
+
the host agent sees a structured error instead of a silent empty answer. The MCP
|
|
89
|
+
framework prefixes the message with its own text, so the envelope travels
|
|
90
|
+
*inside* the error text. `403` is not retried; `429` carries `retry_after` and
|
|
91
|
+
`limit_type` (`rpm` / `daily`) in `details`.
|
|
92
|
+
|
|
93
|
+
Three codes are the SDK's own, because the contract's code list covers
|
|
94
|
+
server-side failures only: `HTTP_<status>` (a response without a code),
|
|
95
|
+
`REQUEST_FAILED` (a response body the SDK could not read) and `TRANSPORT_ERROR`
|
|
96
|
+
(the request never reached the server).
|
|
97
|
+
|
|
98
|
+
## Development
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pip install -e packages/sequenx-py
|
|
102
|
+
pip install -e packages/sequenx-mcp -r packages/sequenx-mcp/requirements-dev.txt
|
|
103
|
+
cd packages/sequenx-mcp
|
|
104
|
+
python -m pytest -q
|
|
105
|
+
python -m ruff check src tests
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Gitee Go runs the suite via `scripts/ci/gitee-verify.sh mcp`.
|
|
109
|
+
|
|
110
|
+
Use a separate virtualenv for this package: `mcp` 2.x installs `httpx2`, and a
|
|
111
|
+
control-plane environment that has `httpx2` next to `httpx` breaks Starlette's
|
|
112
|
+
test client (`services/api` needs `httpx` 1.x).
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
sequenx_mcp/__init__.py,sha256=uDWjETMHAREI7GEutCkQUMeSUKYEcNU3jyKbWHI4FD4,199
|
|
2
|
+
sequenx_mcp/__main__.py,sha256=7PUkI_f04uChGSNpjPu-6l7pdCEkK84spTRzKZagAJY,147
|
|
3
|
+
sequenx_mcp/_version.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
|
|
4
|
+
sequenx_mcp/errors.py,sha256=oCUpvdhAVY1-cL36Zxovp2NS7y6V-MOcpWsyn0_RGmU,2854
|
|
5
|
+
sequenx_mcp/server.py,sha256=EFeJvwsrRwHADbtVH_DhLv56mUxu0TmbNK0JacE6KJY,4548
|
|
6
|
+
sequenx_mcp/tools.py,sha256=B7fWJOMgdDGmUzwZX74VHchT9rIZQNDKlHCzB3fMCfs,2974
|
|
7
|
+
sequenx_mcp-0.1.0.dist-info/METADATA,sha256=60fMaPXf_Q8umMeqQQY6Zq3kqW6xADLvAnD_PQcAqe0,4402
|
|
8
|
+
sequenx_mcp-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
9
|
+
sequenx_mcp-0.1.0.dist-info/top_level.txt,sha256=jP7ZO66U7u9jSljIRK7_notOIeo1uH_Y5xHIc0P8QLo,12
|
|
10
|
+
sequenx_mcp-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sequenx_mcp
|