mcp-server-pixeltable-developer 0.2.1__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.
- mcp_server_pixeltable_developer/__init__.py +11 -0
- mcp_server_pixeltable_developer/__main__.py +29 -0
- mcp_server_pixeltable_developer/_worker.py +51 -0
- mcp_server_pixeltable_developer/canvas.py +403 -0
- mcp_server_pixeltable_developer/conformance.py +45 -0
- mcp_server_pixeltable_developer/models.py +192 -0
- mcp_server_pixeltable_developer/prompts.py +97 -0
- mcp_server_pixeltable_developer/resources.py +134 -0
- mcp_server_pixeltable_developer/runtime.py +324 -0
- mcp_server_pixeltable_developer/server.py +125 -0
- mcp_server_pixeltable_developer/tools.py +700 -0
- mcp_server_pixeltable_developer/unsafe.py +441 -0
- mcp_server_pixeltable_developer-0.2.1.data/data/share/mcp-server-pixeltable-developer/canvas.html +243 -0
- mcp_server_pixeltable_developer-0.2.1.dist-info/METADATA +303 -0
- mcp_server_pixeltable_developer-0.2.1.dist-info/RECORD +21 -0
- mcp_server_pixeltable_developer-0.2.1.dist-info/WHEEL +4 -0
- mcp_server_pixeltable_developer-0.2.1.dist-info/entry_points.txt +2 -0
- mcp_server_pixeltable_developer-0.2.1.dist-info/licenses/LICENSE +201 -0
- mcp_server_pixeltable_stio/__init__.py +14 -0
- mcp_server_pixeltable_stio/__main__.py +6 -0
- mcp_server_pixeltable_stio/server.py +5 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Typed MCP inputs and outputs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field, JsonValue
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StrictModel(BaseModel):
|
|
11
|
+
"""Base model for stable public result contracts."""
|
|
12
|
+
|
|
13
|
+
model_config = ConfigDict(extra="forbid")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CatalogEntry(BaseModel):
|
|
17
|
+
"""One object in the Pixeltable catalog tree."""
|
|
18
|
+
|
|
19
|
+
model_config = ConfigDict(extra="allow")
|
|
20
|
+
|
|
21
|
+
name: str | None = None
|
|
22
|
+
path: str
|
|
23
|
+
kind: str | None = None
|
|
24
|
+
rows: int | None = None
|
|
25
|
+
columns: int | None = None
|
|
26
|
+
version: int | None = None
|
|
27
|
+
error_count: int | None = None
|
|
28
|
+
base: str | None = None
|
|
29
|
+
entries: list[CatalogEntry] = Field(default_factory=list)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CatalogResult(StrictModel):
|
|
33
|
+
"""Catalog entries below a requested path."""
|
|
34
|
+
|
|
35
|
+
path: str
|
|
36
|
+
entries: list[CatalogEntry]
|
|
37
|
+
tree: CatalogEntry | None = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ColumnDescription(BaseModel):
|
|
41
|
+
"""Relevant metadata for a table column."""
|
|
42
|
+
|
|
43
|
+
model_config = ConfigDict(extra="allow")
|
|
44
|
+
|
|
45
|
+
name: str
|
|
46
|
+
type_: str
|
|
47
|
+
is_stored: bool | None = None
|
|
48
|
+
is_primary_key: bool | None = None
|
|
49
|
+
is_computed: bool | None = None
|
|
50
|
+
computed_with: str | None = None
|
|
51
|
+
depends_on: list[list[str]] | None = None
|
|
52
|
+
comment: str | None = None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class TableDescription(BaseModel):
|
|
56
|
+
"""Schema and metadata reported by ``pxt describe``."""
|
|
57
|
+
|
|
58
|
+
model_config = ConfigDict(extra="allow")
|
|
59
|
+
|
|
60
|
+
id: str
|
|
61
|
+
name: str
|
|
62
|
+
path: str
|
|
63
|
+
kind: str
|
|
64
|
+
columns: dict[str, ColumnDescription]
|
|
65
|
+
indexes: dict[str, Any] = Field(default_factory=dict)
|
|
66
|
+
has_default_idxs: bool = False
|
|
67
|
+
is_view: bool = False
|
|
68
|
+
is_snapshot: bool = False
|
|
69
|
+
version: int
|
|
70
|
+
primary_key: list[str] | str | None = None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class RowsResult(StrictModel):
|
|
74
|
+
"""A bounded table preview."""
|
|
75
|
+
|
|
76
|
+
path: str
|
|
77
|
+
rows: list[dict[str, JsonValue]]
|
|
78
|
+
count: int
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class RowResult(StrictModel):
|
|
82
|
+
"""One row selected by primary key."""
|
|
83
|
+
|
|
84
|
+
path: str
|
|
85
|
+
row: dict[str, JsonValue]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ErrorsResult(StrictModel):
|
|
89
|
+
"""Failed computed-column values for a table."""
|
|
90
|
+
|
|
91
|
+
path: str
|
|
92
|
+
column: str | None = None
|
|
93
|
+
errors: list[dict[str, JsonValue]]
|
|
94
|
+
count: int
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class InsertResult(StrictModel):
|
|
98
|
+
"""Pixeltable insertion statistics."""
|
|
99
|
+
|
|
100
|
+
path: str
|
|
101
|
+
num_rows: int
|
|
102
|
+
num_computed_values: int
|
|
103
|
+
num_exceptions: int
|
|
104
|
+
columns_with_exceptions: list[str]
|
|
105
|
+
updated_columns: list[str]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class CheckResult(StrictModel):
|
|
109
|
+
"""Static validation result for a schema or service file."""
|
|
110
|
+
|
|
111
|
+
kind: Literal["schema", "service"]
|
|
112
|
+
file: str
|
|
113
|
+
valid: bool
|
|
114
|
+
errors: list[str]
|
|
115
|
+
warnings: list[str]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class ReconcileResult(StrictModel):
|
|
119
|
+
"""A schema or service reconciliation result."""
|
|
120
|
+
|
|
121
|
+
kind: Literal["schema", "service"]
|
|
122
|
+
operation: Literal["diff", "update", "prune"]
|
|
123
|
+
file: str
|
|
124
|
+
target: str
|
|
125
|
+
pending: bool
|
|
126
|
+
details: JsonValue
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class ServiceInfo(BaseModel):
|
|
130
|
+
"""Stable service fields with forward-compatible CLI metadata."""
|
|
131
|
+
|
|
132
|
+
model_config = ConfigDict(extra="allow")
|
|
133
|
+
|
|
134
|
+
name: str
|
|
135
|
+
catalog_path: str | None = None
|
|
136
|
+
endpoint: str | None = None
|
|
137
|
+
port: int | None = Field(default=None, ge=1, le=65535)
|
|
138
|
+
state: str | None = None
|
|
139
|
+
error: str | None = None
|
|
140
|
+
app_module: str | None = None
|
|
141
|
+
spec: JsonValue = None
|
|
142
|
+
pid: int | None = None
|
|
143
|
+
process_started_at: float | None = None
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class ServiceListResult(StrictModel):
|
|
147
|
+
"""Services known to the Pixeltable daemon."""
|
|
148
|
+
|
|
149
|
+
target: str | None = None
|
|
150
|
+
services: list[ServiceInfo]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class ServiceStopResult(StrictModel):
|
|
154
|
+
"""Services stopped by a request."""
|
|
155
|
+
|
|
156
|
+
names: list[str]
|
|
157
|
+
details: JsonValue
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class ScaffoldResult(StrictModel):
|
|
161
|
+
"""A newly generated application or schema file."""
|
|
162
|
+
|
|
163
|
+
project: str
|
|
164
|
+
kind: Literal["service", "schema"]
|
|
165
|
+
file: str
|
|
166
|
+
created: bool
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class RecomputeResult(StrictModel):
|
|
170
|
+
"""A computed-column recovery plan or execution result."""
|
|
171
|
+
|
|
172
|
+
path: str
|
|
173
|
+
columns: list[str]
|
|
174
|
+
errors_only: bool
|
|
175
|
+
dry_run: bool
|
|
176
|
+
pending: bool
|
|
177
|
+
details: JsonValue
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class StatusResource(StrictModel):
|
|
181
|
+
"""Redacted server and runtime status."""
|
|
182
|
+
|
|
183
|
+
server_name: str
|
|
184
|
+
server_version: str
|
|
185
|
+
mcp_version: str
|
|
186
|
+
pixeltable_version: str
|
|
187
|
+
project: str
|
|
188
|
+
transport: str
|
|
189
|
+
unsafe_tools_enabled: bool
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
JsonObject = dict[str, JsonValue]
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Focused workflow prompts for Pixeltable application development."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated, Any
|
|
6
|
+
|
|
7
|
+
from mcp.server.mcpserver import MCPServer
|
|
8
|
+
from pydantic import Field
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def register_prompts(server: MCPServer[Any]) -> None:
|
|
12
|
+
"""Register the four supported workflow prompts."""
|
|
13
|
+
|
|
14
|
+
@server.prompt(
|
|
15
|
+
name="pixeltable_build_app",
|
|
16
|
+
title="Build a Pixeltable application",
|
|
17
|
+
description="Plan and implement one app.py with TableModel tables and FastAPIRouter routes.",
|
|
18
|
+
)
|
|
19
|
+
def build_app(
|
|
20
|
+
goal: Annotated[str, Field(min_length=1, description="What the application should do.")],
|
|
21
|
+
) -> str:
|
|
22
|
+
return f"""Build a Pixeltable application for this goal:
|
|
23
|
+
|
|
24
|
+
{goal}
|
|
25
|
+
|
|
26
|
+
Read pixeltable://guidance/app first. Work in one source-controlled application file.
|
|
27
|
+
Start from `pxt init` and `pxt service example --out app.py`; declare stored columns
|
|
28
|
+
with annotations, computed columns with assignments, and HTTP routes with
|
|
29
|
+
`FastAPIRouter` imported from `pixeltable.serving`. Validate, diff, and update in
|
|
30
|
+
that order. Use deterministic local fixtures before adding provider calls."""
|
|
31
|
+
|
|
32
|
+
@server.prompt(
|
|
33
|
+
name="pixeltable_build_rag",
|
|
34
|
+
title="Build Pixeltable retrieval",
|
|
35
|
+
description="Design document or multimodal ingestion, chunking, embedding, and retrieval in Pixeltable.",
|
|
36
|
+
)
|
|
37
|
+
def build_rag(
|
|
38
|
+
source_media: Annotated[
|
|
39
|
+
str,
|
|
40
|
+
Field(min_length=1, description="Document, image, video, or audio inputs to ingest."),
|
|
41
|
+
],
|
|
42
|
+
query_behavior: Annotated[
|
|
43
|
+
str,
|
|
44
|
+
Field(min_length=1, description="How callers should search and use the retrieved context."),
|
|
45
|
+
],
|
|
46
|
+
) -> str:
|
|
47
|
+
return f"""Build a Pixeltable retrieval application.
|
|
48
|
+
|
|
49
|
+
Inputs: {source_media}
|
|
50
|
+
Query behavior: {query_behavior}
|
|
51
|
+
|
|
52
|
+
Use a TableModel application and Pixeltable computed columns. Use the appropriate
|
|
53
|
+
document, image, video, or audio iterator; persist chunks in a view; add the
|
|
54
|
+
modality-appropriate embedding index; and call similarity with a named modality
|
|
55
|
+
argument. Keep the catalog as the system of record. Add a FastAPIRouter retrieval
|
|
56
|
+
route, then validate, diff, update, insert deterministic fixtures, and test
|
|
57
|
+
retrieval."""
|
|
58
|
+
|
|
59
|
+
@server.prompt(
|
|
60
|
+
name="pixeltable_build_agent",
|
|
61
|
+
title="Build a Pixeltable tool-calling agent",
|
|
62
|
+
description="Design an agent whose model responses and tool executions are persisted as computed columns.",
|
|
63
|
+
)
|
|
64
|
+
def build_agent(
|
|
65
|
+
objective: Annotated[str, Field(min_length=1, description="The agent's objective.")],
|
|
66
|
+
tools: Annotated[str, Field(min_length=1, description="The tools the model may call.")],
|
|
67
|
+
) -> str:
|
|
68
|
+
return f"""Build a persistent Pixeltable tool-calling agent.
|
|
69
|
+
|
|
70
|
+
Objective: {objective}
|
|
71
|
+
Tools: {tools}
|
|
72
|
+
|
|
73
|
+
Declare request, model response, tool-call, tool-result, and final-response columns
|
|
74
|
+
in a TableModel application. Construct tool definitions with `pxt.tools()`, pass
|
|
75
|
+
them to the provider function, and execute requested calls with `invoke_tools()`.
|
|
76
|
+
Preserve provider outputs in their native type. Add an HTTP route, use mocked
|
|
77
|
+
provider results for local wiring tests, and do not claim live-provider coverage."""
|
|
78
|
+
|
|
79
|
+
@server.prompt(
|
|
80
|
+
name="pixeltable_debug_computation",
|
|
81
|
+
title="Debug a Pixeltable computation",
|
|
82
|
+
description="Inspect failed computed values and build a bounded recovery procedure.",
|
|
83
|
+
)
|
|
84
|
+
def debug_computation(
|
|
85
|
+
table: Annotated[str, Field(min_length=1, description="Table or view path with failures.")],
|
|
86
|
+
column: Annotated[str, Field(min_length=1, description="Computed column to diagnose.")],
|
|
87
|
+
) -> str:
|
|
88
|
+
return f"""Diagnose failures in `{table}.{column}`.
|
|
89
|
+
|
|
90
|
+
Describe the table, list errors filtered to `{column}`, and inspect the input
|
|
91
|
+
columns the expression depends on. Fix the source data, configuration, provider
|
|
92
|
+
credentials, or application code that caused the error. Preview
|
|
93
|
+
`pxt recompute {table} {column} --errors-only -n`; apply it with `-f` only after the
|
|
94
|
+
preview is correct. If the fix changes the computed expression, apply it with a
|
|
95
|
+
schema update, then recompute without `--errors-only`: the update keeps existing
|
|
96
|
+
values. If the column's type must change, rename the column, because an in-place
|
|
97
|
+
type change is unsupported."""
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Focused MCP resources for runtime context and Pixeltable guidance."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from mcp.server.mcpserver import MCPServer
|
|
9
|
+
|
|
10
|
+
from . import __version__
|
|
11
|
+
from .models import CatalogEntry, CatalogResult, StatusResource
|
|
12
|
+
from .runtime import CommandRunner, ServerConfig
|
|
13
|
+
|
|
14
|
+
APP_GUIDANCE = """# Pixeltable application workflow
|
|
15
|
+
|
|
16
|
+
1. Run `pxt init` once in the project root.
|
|
17
|
+
2. Start with `pxt service example --out app.py` (or `pxt schema example --brief --out schema.py`).
|
|
18
|
+
3. Declare tables on `TableModel = pxt.model_base()`. Stored columns use annotations; computed columns use assignments.
|
|
19
|
+
4. Import `FastAPIRouter` from `pixeltable.serving` and declare HTTP routes on it.
|
|
20
|
+
Keep tables, computed columns, and routes in the source-controlled application file.
|
|
21
|
+
5. Validate with `pxt service check app.py`, inspect with
|
|
22
|
+
`pxt service diff app.py TARGET`, then apply with
|
|
23
|
+
`pxt service update app.py TARGET`.
|
|
24
|
+
|
|
25
|
+
Stored columns are non-nullable by default. Write `pxt.String | None` when null is allowed. Do not use `pxt.Required`.
|
|
26
|
+
Create computed columns declaratively. To change a computed expression, edit it and
|
|
27
|
+
run `pxt schema update`: it records the new expression and keeps the stored values,
|
|
28
|
+
so run `pxt recompute TABLE COLUMN -f` to refresh existing rows. Changing a column's
|
|
29
|
+
type is UNSUPPORTED and applies nothing, even with `--allow-destructive`: rename a
|
|
30
|
+
computed column instead, and declare a stored column `T | None` up front.
|
|
31
|
+
Use Pixeltable iterators, embedding indexes, and `pxt.tools()` / `invoke_tools()`
|
|
32
|
+
for multimodal retrieval and agents. Do not add a separate dataframe store or
|
|
33
|
+
vector database.
|
|
34
|
+
For failures, inspect `pxt errors TABLE` and preview
|
|
35
|
+
`pxt recompute TABLE COLUMN --errors-only -n` before applying it with `-f`.
|
|
36
|
+
|
|
37
|
+
Row-addressed operations (`pxt get`, `pxt errors`) require a primary key.
|
|
38
|
+
`pxt service example` generates one: `id = pxt.Column(value=pxtf.uuid.uuid7(), primary_key=True)`,
|
|
39
|
+
computed on insert. Declare a key in every model before its first schema update:
|
|
40
|
+
Pixeltable cannot add a primary key to a table that already exists.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
CLOUD_GUIDANCE = """# Pixeltable Cloud preparation
|
|
45
|
+
|
|
46
|
+
Cloud behavior in this server is source-reviewed but not live-tested. Review each diff before applying it.
|
|
47
|
+
|
|
48
|
+
1. Sign in with `pxt login`, or export `PIXELTABLE_API_KEY`, which wins over a sign-in.
|
|
49
|
+
2. Add `[[pixeltable.database]]` with `name = 'pxt://ORG:DATABASE'` to `pixeltable.toml`.
|
|
50
|
+
3. Set provider keys with `pxt secret set pxt://ORG OPENAI_API_KEY=...`. Never put
|
|
51
|
+
secrets in `pixeltable.toml`: a database entry with a `secrets` mapping fails to load.
|
|
52
|
+
4. Run `pxt db diff`, then `pxt db update pxt://ORG:DATABASE -f`. The first update creates the database.
|
|
53
|
+
5. Run `pxt schema diff` and `pxt schema update` against the hosted target.
|
|
54
|
+
6. Run `pxt service diff` and `pxt service update` only after the database code and schema are current.
|
|
55
|
+
|
|
56
|
+
The MCP schema and service tools accept explicit `pxt://` targets. They never
|
|
57
|
+
create databases or set secrets. A hosted `schema_update` or `service_update` call
|
|
58
|
+
changes that hosted target, so inspect the corresponding diff first.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _distribution_version(name: str) -> str:
|
|
63
|
+
try:
|
|
64
|
+
return version(name)
|
|
65
|
+
except PackageNotFoundError:
|
|
66
|
+
return "not-installed"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def register_resources(server: MCPServer[Any], config: ServerConfig, runner: CommandRunner) -> None:
|
|
70
|
+
"""Register the four supported resources."""
|
|
71
|
+
|
|
72
|
+
@server.resource(
|
|
73
|
+
"pixeltable://status",
|
|
74
|
+
name="pixeltable_status",
|
|
75
|
+
title="Pixeltable MCP status",
|
|
76
|
+
description="Redacted server, package, project, and feature-mode versions.",
|
|
77
|
+
mime_type="application/json",
|
|
78
|
+
)
|
|
79
|
+
def status_resource() -> str:
|
|
80
|
+
status = StatusResource(
|
|
81
|
+
server_name="pixeltable-developer",
|
|
82
|
+
server_version=__version__,
|
|
83
|
+
mcp_version=_distribution_version("mcp"),
|
|
84
|
+
pixeltable_version=_distribution_version("pixeltable"),
|
|
85
|
+
project=config.project_root.name,
|
|
86
|
+
transport=config.transport,
|
|
87
|
+
unsafe_tools_enabled=config.unsafe_enabled,
|
|
88
|
+
)
|
|
89
|
+
return status.model_dump_json()
|
|
90
|
+
|
|
91
|
+
@server.resource(
|
|
92
|
+
"pixeltable://catalog",
|
|
93
|
+
name="pixeltable_catalog",
|
|
94
|
+
title="Pixeltable catalog",
|
|
95
|
+
description="Tables, views, and directories in the configured local catalog.",
|
|
96
|
+
mime_type="application/json",
|
|
97
|
+
)
|
|
98
|
+
async def catalog_resource() -> str:
|
|
99
|
+
result = await runner.pxt(["ls", "--tree", "--json"])
|
|
100
|
+
if not isinstance(result.data, dict):
|
|
101
|
+
raise RuntimeError("Pixeltable returned an unexpected catalog listing")
|
|
102
|
+
tree_payload = result.data.get("tree")
|
|
103
|
+
entries_payload = result.data.get("entries", [])
|
|
104
|
+
if isinstance(tree_payload, dict):
|
|
105
|
+
entries_payload = tree_payload.get("entries", entries_payload)
|
|
106
|
+
if not isinstance(entries_payload, list):
|
|
107
|
+
raise RuntimeError("Pixeltable returned an unexpected catalog listing")
|
|
108
|
+
tree = CatalogEntry.model_validate(tree_payload) if tree_payload is not None else None
|
|
109
|
+
catalog = CatalogResult(
|
|
110
|
+
path="",
|
|
111
|
+
entries=[CatalogEntry.model_validate(entry) for entry in entries_payload],
|
|
112
|
+
tree=tree,
|
|
113
|
+
)
|
|
114
|
+
return catalog.model_dump_json()
|
|
115
|
+
|
|
116
|
+
@server.resource(
|
|
117
|
+
"pixeltable://guidance/app",
|
|
118
|
+
name="pixeltable_application_guidance",
|
|
119
|
+
title="Pixeltable application guidance",
|
|
120
|
+
description="Application-first workflow aligned with Pixeltable 0.7.16 and skill 2.12.0.",
|
|
121
|
+
mime_type="text/markdown",
|
|
122
|
+
)
|
|
123
|
+
def application_guidance_resource() -> str:
|
|
124
|
+
return APP_GUIDANCE
|
|
125
|
+
|
|
126
|
+
@server.resource(
|
|
127
|
+
"pixeltable://guidance/cloud",
|
|
128
|
+
name="pixeltable_cloud_guidance",
|
|
129
|
+
title="Pixeltable Cloud preparation",
|
|
130
|
+
description="Reviewed deployment ordering and configuration guidance; not live-tested.",
|
|
131
|
+
mime_type="text/markdown",
|
|
132
|
+
)
|
|
133
|
+
def cloud_guidance_resource() -> str:
|
|
134
|
+
return CLOUD_GUIDANCE
|