interloper-toolkit 0.89.0__tar.gz → 0.92.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.
Files changed (22) hide show
  1. interloper_toolkit-0.92.0/PKG-INFO +49 -0
  2. interloper_toolkit-0.92.0/README.md +38 -0
  3. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/pyproject.toml +2 -2
  4. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/pyproject.toml.orig +2 -2
  5. interloper_toolkit-0.92.0/src/interloper_toolkit/__init__.py +34 -0
  6. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/collection.py +4 -4
  7. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/errors.py +3 -3
  8. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/models.py +27 -1
  9. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/scheduling.py +4 -1
  10. interloper_toolkit-0.92.0/src/interloper_toolkit/tools.py +158 -0
  11. interloper_toolkit-0.89.0/PKG-INFO +0 -40
  12. interloper_toolkit-0.89.0/README.md +0 -29
  13. interloper_toolkit-0.89.0/src/interloper_toolkit/__init__.py +0 -22
  14. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/analytics.py +0 -0
  15. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/authz.py +0 -0
  16. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/catalog.py +0 -0
  17. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/context.py +0 -0
  18. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/jobs.py +0 -0
  19. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/lineage.py +0 -0
  20. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/sources.py +0 -0
  21. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/stats.py +0 -0
  22. {interloper_toolkit-0.89.0 → interloper_toolkit-0.92.0}/src/interloper_toolkit/utils.py +0 -0
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.3
2
+ Name: interloper-toolkit
3
+ Version: 0.92.0
4
+ Summary: Interloper tool functions shared by the AI surfaces (agent, MCP)
5
+ Author: Guillaume Onfroy
6
+ Author-email: Guillaume Onfroy <guillaume@digitlcloud.com>
7
+ Requires-Dist: interloper-core
8
+ Requires-Dist: interloper-db
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+
12
+ # interloper-toolkit
13
+
14
+ The tool functions shared by interloper's AI surfaces: the chat agent
15
+ (`interloper-agent`) and the MCP server (`interloper-mcp`).
16
+
17
+ Every function takes a frozen `ToolkitContext(store, catalog, org_id, role)`
18
+ as its first argument and returns `<SuccessModel> | ToolError`: typed
19
+ pydantic results (see `models.py`) discriminated by the literal `status`
20
+ field, never raising. The models are the tool contract: MCP derives per-tool
21
+ output schemas from them, row-projecting models act as an allowlist of what
22
+ leaves the platform, and full-row payloads embed the interloper-db models to
23
+ keep that coupling visible rather than duplicated. The docstrings are
24
+ LLM-facing: both surfaces adopt them verbatim as tool descriptions.
25
+
26
+ Reads take no role; writes declare the role they need with `requires_role`
27
+ and refuse below it, as a structured `ToolError`.
28
+
29
+ `tools.TOOLS` is the table both surfaces register: every tool with its
30
+ `Effect` (read, read through a provider, edit, create, launch, cancel). A
31
+ surface derives its behaviour from the effect (MCP annotations, the agent's
32
+ approvals) and `Tool.bind` adapts a function to the way the surface supplies
33
+ the context. A tool that `carries_secrets` in its arguments is left off any
34
+ surface that transports arguments through a third party.
35
+
36
+ Modules:
37
+
38
+ - `catalog`: the component definitions the platform ships (list, detail,
39
+ asset schemas, field search, schema comparison)
40
+ - `collection`: the org's component instances (listing, edits, relations,
41
+ connection checks and setup; sensitive kinds project identity only)
42
+ - `sources` and `jobs`: creating sources, resolving their fields, creating jobs
43
+ - `lineage`: dependency analysis, impact assessment, DAG traversal
44
+ - `scheduling`: jobs, runs and backfills, monitoring and control
45
+ - `analytics`: run statistics, partition coverage, data freshness
46
+ - `tools`: the table above
47
+
48
+ Depends only on `interloper-core` and `interloper-db`; no LLM-framework
49
+ dependencies (no pydantic-ai, no mcp).
@@ -0,0 +1,38 @@
1
+ # interloper-toolkit
2
+
3
+ The tool functions shared by interloper's AI surfaces: the chat agent
4
+ (`interloper-agent`) and the MCP server (`interloper-mcp`).
5
+
6
+ Every function takes a frozen `ToolkitContext(store, catalog, org_id, role)`
7
+ as its first argument and returns `<SuccessModel> | ToolError`: typed
8
+ pydantic results (see `models.py`) discriminated by the literal `status`
9
+ field, never raising. The models are the tool contract: MCP derives per-tool
10
+ output schemas from them, row-projecting models act as an allowlist of what
11
+ leaves the platform, and full-row payloads embed the interloper-db models to
12
+ keep that coupling visible rather than duplicated. The docstrings are
13
+ LLM-facing: both surfaces adopt them verbatim as tool descriptions.
14
+
15
+ Reads take no role; writes declare the role they need with `requires_role`
16
+ and refuse below it, as a structured `ToolError`.
17
+
18
+ `tools.TOOLS` is the table both surfaces register: every tool with its
19
+ `Effect` (read, read through a provider, edit, create, launch, cancel). A
20
+ surface derives its behaviour from the effect (MCP annotations, the agent's
21
+ approvals) and `Tool.bind` adapts a function to the way the surface supplies
22
+ the context. A tool that `carries_secrets` in its arguments is left off any
23
+ surface that transports arguments through a third party.
24
+
25
+ Modules:
26
+
27
+ - `catalog`: the component definitions the platform ships (list, detail,
28
+ asset schemas, field search, schema comparison)
29
+ - `collection`: the org's component instances (listing, edits, relations,
30
+ connection checks and setup; sensitive kinds project identity only)
31
+ - `sources` and `jobs`: creating sources, resolving their fields, creating jobs
32
+ - `lineage`: dependency analysis, impact assessment, DAG traversal
33
+ - `scheduling`: jobs, runs and backfills, monitoring and control
34
+ - `analytics`: run statistics, partition coverage, data freshness
35
+ - `tools`: the table above
36
+
37
+ Depends only on `interloper-core` and `interloper-db`; no LLM-framework
38
+ dependencies (no pydantic-ai, no mcp).
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "interloper-toolkit"
3
- version = "0.89.0"
4
- description = "Interloper shared read-only tool functions for AI surfaces (agent, MCP)"
3
+ version = "0.92.0"
4
+ description = "Interloper tool functions shared by the AI surfaces (agent, MCP)"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
7
7
  dependencies = [
@@ -3,8 +3,8 @@
3
3
  # ###############
4
4
  [project]
5
5
  name = "interloper-toolkit"
6
- version = "0.89.0"
7
- description = "Interloper shared read-only tool functions for AI surfaces (agent, MCP)"
6
+ version = "0.92.0"
7
+ description = "Interloper tool functions shared by the AI surfaces (agent, MCP)"
8
8
  readme = "README.md"
9
9
  authors = [{ name = "Guillaume Onfroy", email = "guillaume@digitlcloud.com" }]
10
10
  requires-python = ">=3.10"
@@ -0,0 +1,34 @@
1
+ """Tool functions shared by the AI surfaces (agent, MCP server).
2
+
3
+ Every function takes a :class:`~interloper_toolkit.context.ToolkitContext`
4
+ as its first argument and returns ``<SuccessModel> | ToolError``: typed
5
+ pydantic results (see :mod:`interloper_toolkit.models`) discriminated by
6
+ the literal ``status`` field, never raising. The docstrings are LLM-facing:
7
+ the surfaces adopt them verbatim as tool descriptions.
8
+
9
+ Reads take no role; writes declare the role they need with
10
+ :func:`~interloper_toolkit.authz.requires_role` and refuse below it.
11
+ :data:`~interloper_toolkit.tools.TOOLS` is the table the surfaces register,
12
+ each tool with its :class:`~interloper_toolkit.tools.Effect`; a surface
13
+ derives its behaviour from that and leaves out what it cannot carry (the
14
+ MCP server never registers a tool whose arguments carry credentials).
15
+ """
16
+
17
+ from interloper_toolkit.authz import requires_role
18
+ from interloper_toolkit.collection import bind_relation, unbind_relation
19
+ from interloper_toolkit.context import ToolkitContext, serialize
20
+ from interloper_toolkit.models import ToolError, UserRequest
21
+ from interloper_toolkit.tools import TOOLS, Effect, Tool
22
+
23
+ __all__ = [
24
+ "TOOLS",
25
+ "Effect",
26
+ "Tool",
27
+ "ToolError",
28
+ "ToolkitContext",
29
+ "UserRequest",
30
+ "bind_relation",
31
+ "requires_role",
32
+ "serialize",
33
+ "unbind_relation",
34
+ ]
@@ -17,7 +17,7 @@ from typing import Any
17
17
  from urllib.parse import urlencode
18
18
  from uuid import UUID
19
19
 
20
- import httpx
20
+ import httpx2
21
21
  from interloper.component import KINDS
22
22
  from interloper.connection.base import Connection
23
23
  from interloper.errors import (
@@ -466,12 +466,12 @@ def categorise(exc: Exception) -> tuple[str, str]:
466
466
  """
467
467
  if isinstance(exc, ConnectionCheckError):
468
468
  return "error", str(exc)
469
- if isinstance(exc, httpx.HTTPStatusError):
469
+ if isinstance(exc, httpx2.HTTPStatusError):
470
470
  if exc.response.status_code in (401, 403):
471
471
  return "auth", "The provider rejected the credentials."
472
472
  return "error", f"The provider responded with HTTP {exc.response.status_code}."
473
- if isinstance(exc, (TimeoutError, httpx.TimeoutException)):
473
+ if isinstance(exc, (TimeoutError, httpx2.TimeoutException)):
474
474
  return "network", "The provider did not respond in time."
475
- if isinstance(exc, httpx.TransportError):
475
+ if isinstance(exc, httpx2.TransportError):
476
476
  return "network", "The provider could not be reached."
477
477
  return "error", "The connection check failed unexpectedly."
@@ -3,7 +3,7 @@
3
3
  Every error the platform records went through
4
4
  :func:`interloper.errors.format_exception`, so it opens with the exception's
5
5
  type name; what follows is the library's own message. Three families carry
6
- an HTTP request in a recognisable shape: httpx (``Client error '429 …' for
6
+ an HTTP request in a recognisable shape: httpx2 (``Client error '429 …' for
7
7
  url '…'``), google-api-core (``403 POST https://…: message``) and the
8
8
  Facebook SDK (``Method:`` / ``Path:`` / ``Status:`` lines over a JSON body
9
9
  with ``code`` and ``error_subcode``). Everything else is grouped by its
@@ -49,8 +49,8 @@ def classify(text: str) -> ErrorCause:
49
49
 
50
50
  status: int | None = None
51
51
  method: str | None = None
52
- if httpx := _HTTPX_STATUS.search(message):
53
- status = int(httpx.group(1))
52
+ if httpx2 := _HTTPX_STATUS.search(message):
53
+ status = int(httpx2.group(1))
54
54
  elif google := _GOOGLE_STATUS.match(message.strip()):
55
55
  status = int(google.group(1))
56
56
  method = google.group(2)
@@ -228,7 +228,24 @@ class ConnectionsCreated(BaseModel):
228
228
  failed: list[FailedInstance]
229
229
 
230
230
 
231
- class ConnectionSetup(BaseModel):
231
+ class UserRequest(BaseModel):
232
+ """A result that asks the user for something the model cannot supply.
233
+
234
+ An interactive surface takes it to the user and answers the call with
235
+ their input; elsewhere it is an ordinary result the model relays.
236
+ """
237
+
238
+ @property
239
+ def awaits_user(self) -> bool:
240
+ """Whether the user is being asked.
241
+
242
+ Returns:
243
+ True unless the request resolved in place after all.
244
+ """
245
+ return True
246
+
247
+
248
+ class ConnectionSetup(UserRequest):
232
249
  """The hand-off ``request_connection_setup`` makes.
233
250
 
234
251
  ``setup_url`` opens the app's connection form for this definition, when
@@ -246,6 +263,15 @@ class ConnectionSetup(BaseModel):
246
263
  setup_url: str | None = None
247
264
  existing: list[ComponentRef] = []
248
265
 
266
+ @property
267
+ def awaits_user(self) -> bool:
268
+ """Whether the form is presented.
269
+
270
+ Returns:
271
+ False when the existing connections were listed instead.
272
+ """
273
+ return not self.existing
274
+
249
275
 
250
276
  class ConnectionCheck(BaseModel):
251
277
  """The outcome of a connection's health check.
@@ -229,7 +229,10 @@ def retry_run(ctx: ToolkitContext, run_id: str, scope: str = "all") -> RunRetrie
229
229
  the operations that failed or were canceled.
230
230
 
231
231
  Returns the queued attempt; its ``attempt`` number and ``root_run_id``
232
- tie it to the run it retries. A run that is not failed cannot be retried.
232
+ tie it to the run it retries. The retry continues from the stack's latest
233
+ attempt, so retrying an earlier attempt retries the stack. A run that is
234
+ not failed, or a stack whose latest attempt is pending or succeeded,
235
+ cannot be retried.
233
236
  """
234
237
  try:
235
238
  ctx.store.runs.get(UUID(run_id), org_id=ctx.org_id)
@@ -0,0 +1,158 @@
1
+ """The tool table: every tool the AI surfaces expose, with its effect on the platform.
2
+
3
+ A surface (the agent, the MCP server) registers this table rather than a
4
+ list of its own. The effect is the one fact a surface derives its behaviour
5
+ from: MCP maps it to the annotations a client keys on, the agent to which
6
+ calls wait for the user's approval. ``carries_secrets`` marks a tool whose
7
+ arguments would carry credentials; a surface that transports arguments
8
+ through a third party leaves it out.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import inspect
14
+ import types
15
+ import typing
16
+ from collections.abc import Callable
17
+ from dataclasses import dataclass
18
+ from enum import Enum
19
+ from typing import Any
20
+
21
+ from interloper_toolkit import analytics, catalog, collection, jobs, lineage, scheduling, sources
22
+ from interloper_toolkit.context import ToolkitContext
23
+
24
+
25
+ class Effect(str, Enum):
26
+ """What a tool does to the platform."""
27
+
28
+ READ = "read"
29
+ READ_PROVIDER = "read_provider"
30
+ EDIT = "edit"
31
+ CREATE = "create"
32
+ LAUNCH = "launch"
33
+ CANCEL = "cancel"
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class Tool:
38
+ """A toolkit function as the surfaces expose it.
39
+
40
+ Args:
41
+ fn: The toolkit function, sync or async, taking the context first.
42
+ effect: What it does to the platform.
43
+ carries_secrets: Whether its arguments carry credentials.
44
+ """
45
+
46
+ fn: types.FunctionType
47
+ effect: Effect
48
+ carries_secrets: bool = False
49
+
50
+ @property
51
+ def name(self) -> str:
52
+ """The tool's name, which is the function's.
53
+
54
+ Returns:
55
+ The function name.
56
+ """
57
+ return self.fn.__name__
58
+
59
+ @property
60
+ def needs_approval(self) -> bool:
61
+ """Whether an interactive surface waits for the user before the call runs.
62
+
63
+ Returns:
64
+ True for creates and cancels, the calls a user wants to confirm.
65
+ """
66
+ return self.effect in (Effect.CREATE, Effect.CANCEL)
67
+
68
+ def bind(self, context: Callable[..., ToolkitContext], *leading: inspect.Parameter) -> types.FunctionType:
69
+ """Adapt the function to a surface that supplies the context itself.
70
+
71
+ The bound function keeps the name, docstring and every parameter but
72
+ the context, so the schema a model sees is exactly the toolkit's.
73
+ ``leading`` are the surface's own parameters in the context's place,
74
+ if any; ``context`` receives their values and returns the context to
75
+ call with.
76
+
77
+ Args:
78
+ context: Derives the toolkit context from the leading arguments.
79
+ *leading: Parameters the bound function takes before the toolkit's own.
80
+
81
+ Returns:
82
+ The bound function, with an explicit signature and annotations.
83
+ """
84
+ signature = inspect.signature(self.fn)
85
+ hints = typing.get_type_hints(self.fn)
86
+ _, *rest = signature.parameters
87
+ parameters = [*leading, *(signature.parameters[name].replace(annotation=hints[name]) for name in rest)]
88
+ returns = hints.get("return", Any)
89
+ split = len(leading)
90
+
91
+ if inspect.iscoroutinefunction(self.fn):
92
+
93
+ async def tool(*args: Any, **kwargs: Any) -> Any:
94
+ return await self.fn(context(*args[:split]), *args[split:], **kwargs)
95
+ else:
96
+
97
+ def tool(*args: Any, **kwargs: Any) -> Any:
98
+ return self.fn(context(*args[:split]), *args[split:], **kwargs)
99
+
100
+ tool.__name__ = tool.__qualname__ = self.name
101
+ tool.__module__ = self.fn.__module__
102
+ tool.__doc__ = self.fn.__doc__
103
+ tool.__signature__ = signature.replace(parameters=parameters, return_annotation=returns) # ty: ignore[invalid-assignment]
104
+ tool.__annotations__ = {p.name: p.annotation for p in parameters} | {"return": returns}
105
+ return tool
106
+
107
+
108
+ TOOLS: tuple[Tool, ...] = (
109
+ # Catalog
110
+ Tool(catalog.list_definitions, Effect.READ),
111
+ Tool(catalog.get_definition, Effect.READ),
112
+ Tool(catalog.get_asset_schema, Effect.READ),
113
+ Tool(catalog.search_fields, Effect.READ),
114
+ Tool(catalog.compare_schemas, Effect.READ),
115
+ # Collection
116
+ Tool(collection.list_components, Effect.READ),
117
+ Tool(collection.update_component, Effect.EDIT),
118
+ Tool(collection.bind_relation, Effect.EDIT),
119
+ Tool(collection.unbind_relation, Effect.EDIT),
120
+ Tool(collection.request_connection_setup, Effect.READ),
121
+ Tool(collection.check_connection, Effect.READ_PROVIDER),
122
+ Tool(collection.create_connections, Effect.CREATE, carries_secrets=True),
123
+ # Sources and jobs
124
+ Tool(sources.resolve_source_field_options, Effect.READ_PROVIDER),
125
+ Tool(sources.create_source, Effect.CREATE),
126
+ Tool(sources.create_sources, Effect.CREATE),
127
+ Tool(jobs.create_job, Effect.CREATE),
128
+ # Lineage
129
+ Tool(lineage.get_upstream, Effect.READ),
130
+ Tool(lineage.get_downstream, Effect.READ),
131
+ Tool(lineage.get_full_lineage, Effect.READ),
132
+ Tool(lineage.impact_analysis, Effect.READ),
133
+ Tool(lineage.cross_source_dependencies, Effect.READ),
134
+ # Scheduling
135
+ Tool(scheduling.list_jobs, Effect.READ),
136
+ Tool(scheduling.get_job_health, Effect.READ),
137
+ Tool(scheduling.toggle_job, Effect.EDIT),
138
+ Tool(scheduling.toggle_asset, Effect.EDIT),
139
+ Tool(scheduling.list_recent_runs, Effect.READ),
140
+ Tool(scheduling.get_run_detail, Effect.READ),
141
+ Tool(scheduling.list_run_events, Effect.READ),
142
+ Tool(scheduling.get_event, Effect.READ),
143
+ Tool(scheduling.list_failures, Effect.READ),
144
+ Tool(scheduling.error_breakdown, Effect.READ),
145
+ Tool(scheduling.trigger_run, Effect.LAUNCH),
146
+ Tool(scheduling.retry_run, Effect.LAUNCH),
147
+ Tool(scheduling.list_backfills, Effect.READ),
148
+ Tool(scheduling.backfill_timeline, Effect.READ),
149
+ Tool(scheduling.trigger_backfill, Effect.LAUNCH),
150
+ Tool(scheduling.cancel_backfill, Effect.CANCEL),
151
+ # Analytics
152
+ Tool(analytics.run_history_summary, Effect.READ),
153
+ Tool(analytics.partition_coverage, Effect.READ),
154
+ Tool(analytics.freshness_check, Effect.READ),
155
+ Tool(analytics.run_stats, Effect.READ),
156
+ Tool(analytics.asset_coverage, Effect.READ),
157
+ )
158
+ """Every tool the surfaces expose."""
@@ -1,40 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: interloper-toolkit
3
- Version: 0.89.0
4
- Summary: Interloper shared read-only tool functions for AI surfaces (agent, MCP)
5
- Author: Guillaume Onfroy
6
- Author-email: Guillaume Onfroy <guillaume@digitlcloud.com>
7
- Requires-Dist: interloper-core
8
- Requires-Dist: interloper-db
9
- Requires-Python: >=3.10
10
- Description-Content-Type: text/markdown
11
-
12
- # interloper-toolkit
13
-
14
- The read-only tool functions shared by interloper's AI surfaces — the ADK
15
- chat agent (`interloper-agent`) and the MCP server (`interloper-mcp`).
16
-
17
- Every function takes a frozen `ToolkitContext(store, catalog, org_id)` as its
18
- first argument and returns `<SuccessModel> | ToolError` — typed pydantic
19
- results (see `models.py`) discriminated by the literal `status` field, never
20
- raising. The models are the tool contract: MCP derives per-tool output
21
- schemas from them, row-projecting models act as an allowlist of what leaves
22
- the platform, and full-row payloads embed the interloper-db models to keep
23
- that coupling visible rather than duplicated. The docstrings are LLM-facing:
24
- both consumers surface them verbatim as tool descriptions.
25
-
26
- Modules:
27
-
28
- - `catalog` — the component definitions the platform ships (list, detail,
29
- asset schemas, field search, schema comparison)
30
- - `collection` — the org's component instances (read-only listing; sensitive
31
- kinds project identity/metadata only)
32
- - `lineage` — dependency analysis, impact assessment, DAG traversal
33
- - `scheduling` — jobs, runs, and backfills: read-only monitoring
34
- - `analytics` — run statistics, partition coverage, data freshness
35
-
36
- Mutating operations (trigger/toggle/create) deliberately live with the agent,
37
- not here — this package is safe to expose on read-only surfaces.
38
-
39
- Depends only on `interloper-core` and `interloper-db`; no LLM-framework
40
- dependencies (no google-adk, no mcp).
@@ -1,29 +0,0 @@
1
- # interloper-toolkit
2
-
3
- The read-only tool functions shared by interloper's AI surfaces — the ADK
4
- chat agent (`interloper-agent`) and the MCP server (`interloper-mcp`).
5
-
6
- Every function takes a frozen `ToolkitContext(store, catalog, org_id)` as its
7
- first argument and returns `<SuccessModel> | ToolError` — typed pydantic
8
- results (see `models.py`) discriminated by the literal `status` field, never
9
- raising. The models are the tool contract: MCP derives per-tool output
10
- schemas from them, row-projecting models act as an allowlist of what leaves
11
- the platform, and full-row payloads embed the interloper-db models to keep
12
- that coupling visible rather than duplicated. The docstrings are LLM-facing:
13
- both consumers surface them verbatim as tool descriptions.
14
-
15
- Modules:
16
-
17
- - `catalog` — the component definitions the platform ships (list, detail,
18
- asset schemas, field search, schema comparison)
19
- - `collection` — the org's component instances (read-only listing; sensitive
20
- kinds project identity/metadata only)
21
- - `lineage` — dependency analysis, impact assessment, DAG traversal
22
- - `scheduling` — jobs, runs, and backfills: read-only monitoring
23
- - `analytics` — run statistics, partition coverage, data freshness
24
-
25
- Mutating operations (trigger/toggle/create) deliberately live with the agent,
26
- not here — this package is safe to expose on read-only surfaces.
27
-
28
- Depends only on `interloper-core` and `interloper-db`; no LLM-framework
29
- dependencies (no google-adk, no mcp).
@@ -1,22 +0,0 @@
1
- """Tool functions shared by AI surfaces (agent, MCP server).
2
-
3
- Every function takes a :class:`~interloper_toolkit.context.ToolkitContext`
4
- as its first argument and returns ``<SuccessModel> | ToolError`` — typed
5
- pydantic results (see :mod:`interloper_toolkit.models`) discriminated by
6
- the literal ``status`` field, never raising. The docstrings are LLM-facing:
7
- both the ADK agent and the MCP server surface them verbatim as tool
8
- descriptions.
9
-
10
- Reads take no role; writes declare the role they need with
11
- :func:`~interloper_toolkit.authz.requires_role` and refuse below it. Which
12
- functions a surface exposes is that surface's registration list (the MCP
13
- server, for one, never registers ``create_connections``, whose arguments
14
- would carry credentials through the client).
15
- """
16
-
17
- from interloper_toolkit.authz import requires_role
18
- from interloper_toolkit.collection import bind_relation, unbind_relation
19
- from interloper_toolkit.context import ToolkitContext, serialize
20
- from interloper_toolkit.models import ToolError
21
-
22
- __all__ = ["ToolError", "ToolkitContext", "bind_relation", "requires_role", "serialize", "unbind_relation"]