xrefkit 0.3.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.
- xrefkit/__init__.py +5 -0
- xrefkit/__main__.py +5 -0
- xrefkit/catalog_cli.py +57 -0
- xrefkit/cli.py +71 -0
- xrefkit/contracts.py +297 -0
- xrefkit/ctx.py +160 -0
- xrefkit/dashboard.py +1220 -0
- xrefkit/discovery.py +85 -0
- xrefkit/gate.py +428 -0
- xrefkit/goalstate.py +555 -0
- xrefkit/hashing.py +18 -0
- xrefkit/import_skill.py +469 -0
- xrefkit/instance.py +133 -0
- xrefkit/loaders.py +77 -0
- xrefkit/mcp/__init__.py +26 -0
- xrefkit/mcp/audit.py +168 -0
- xrefkit/mcp/bootstrap.py +337 -0
- xrefkit/mcp/catalog.py +2638 -0
- xrefkit/mcp/cli.py +173 -0
- xrefkit/mcp/client_cache.py +356 -0
- xrefkit/mcp/context_registry.py +277 -0
- xrefkit/mcp/contracts.py +243 -0
- xrefkit/mcp/dist.py +234 -0
- xrefkit/mcp/ownership.py +246 -0
- xrefkit/mcp/repository.py +217 -0
- xrefkit/mcp/schemas.py +349 -0
- xrefkit/mcp/server.py +773 -0
- xrefkit/mcp/startup_contract_pack.py +154 -0
- xrefkit/mcp_tools.py +47 -0
- xrefkit/models/__init__.py +41 -0
- xrefkit/models/common.py +185 -0
- xrefkit/models/effective_bundle.py +131 -0
- xrefkit/models/local_manifest.py +217 -0
- xrefkit/models/package_manifest.py +126 -0
- xrefkit/models/run_log.py +276 -0
- xrefkit/models/server_config.py +160 -0
- xrefkit/models/skill_definition.py +131 -0
- xrefkit/operations_cli.py +670 -0
- xrefkit/ownership.py +276 -0
- xrefkit/packmeta.py +289 -0
- xrefkit/registry.py +334 -0
- xrefkit/resolver.py +252 -0
- xrefkit/resource_provider.py +187 -0
- xrefkit/resources/base/contracts.json +178 -0
- xrefkit/resources/base/current.json +6 -0
- xrefkit/resources/base/generations/7a682a5272907354/contracts.json +178 -0
- xrefkit/resources/base/generations/7a682a5272907354/model_body.md +22 -0
- xrefkit/resources/base/generations/9929294385ccb7b0/contracts.json +178 -0
- xrefkit/resources/base/generations/9929294385ccb7b0/model_body.md +22 -0
- xrefkit/resources/base/model_body.md +22 -0
- xrefkit/runlog.py +45 -0
- xrefkit/skillmeta.py +1034 -0
- xrefkit/skillrun.py +2381 -0
- xrefkit/structure_catalog.py +199 -0
- xrefkit/tools/__init__.py +119 -0
- xrefkit/tools/__main__.py +4 -0
- xrefkit/v2_cli.py +130 -0
- xrefkit/workspace.py +117 -0
- xrefkit/xref.py +1048 -0
- xrefkit-0.3.0.dist-info/METADATA +203 -0
- xrefkit-0.3.0.dist-info/RECORD +65 -0
- xrefkit-0.3.0.dist-info/WHEEL +5 -0
- xrefkit-0.3.0.dist-info/entry_points.txt +2 -0
- xrefkit-0.3.0.dist-info/licenses/LICENSE +21 -0
- xrefkit-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
REUSE_SAME_VERSION_REASON = "same_xid_version_already_visible_in_active_context"
|
|
8
|
+
VERSION_CHANGE_REASON = "same_xid_different_content_hash_visible_in_active_context"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class XidContextKey:
|
|
13
|
+
repository_fingerprint: str
|
|
14
|
+
xid: str
|
|
15
|
+
content_hash: str
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def from_document(cls, document: dict[str, Any]) -> "XidContextKey":
|
|
19
|
+
try:
|
|
20
|
+
return cls(
|
|
21
|
+
repository_fingerprint=str(document["repository_fingerprint"]),
|
|
22
|
+
xid=str(document["xid"]),
|
|
23
|
+
content_hash=str(document["content_hash"]),
|
|
24
|
+
)
|
|
25
|
+
except KeyError as exc:
|
|
26
|
+
raise ValueError(
|
|
27
|
+
"XID context documents must include repository_fingerprint, xid, and content_hash"
|
|
28
|
+
) from exc
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class XidContextEntry:
|
|
33
|
+
key: XidContextKey
|
|
34
|
+
visible_in_active_context: bool = False
|
|
35
|
+
injected_turns: list[str] = field(default_factory=list)
|
|
36
|
+
last_injected_turn: str | None = None
|
|
37
|
+
reuse_reasons: list[str] = field(default_factory=list)
|
|
38
|
+
context_entry_id: str | None = None
|
|
39
|
+
materialized_count: int = 0
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SessionXidContextRegistry:
|
|
43
|
+
"""Tracks which XID document bodies are visible in one client session."""
|
|
44
|
+
|
|
45
|
+
def __init__(self) -> None:
|
|
46
|
+
self._entries: dict[XidContextKey, XidContextEntry] = {}
|
|
47
|
+
self._entry_sequence = 0
|
|
48
|
+
self.version_changes: list[dict[str, str]] = []
|
|
49
|
+
|
|
50
|
+
def register_materialized(self, document: dict[str, Any]) -> XidContextEntry:
|
|
51
|
+
key = XidContextKey.from_document(document)
|
|
52
|
+
entry = self._entries.get(key)
|
|
53
|
+
if entry is None:
|
|
54
|
+
entry = XidContextEntry(key=key)
|
|
55
|
+
self._entries[key] = entry
|
|
56
|
+
entry.materialized_count += 1
|
|
57
|
+
return entry
|
|
58
|
+
|
|
59
|
+
def should_inject_body(
|
|
60
|
+
self,
|
|
61
|
+
document: dict[str, Any],
|
|
62
|
+
active_task: str,
|
|
63
|
+
active_workflow: dict[str, Any] | str | None = None,
|
|
64
|
+
active_skill: dict[str, Any] | str | None = None,
|
|
65
|
+
) -> bool:
|
|
66
|
+
key = XidContextKey.from_document(document)
|
|
67
|
+
entry = self._entries.get(key)
|
|
68
|
+
if entry and entry.visible_in_active_context:
|
|
69
|
+
return False
|
|
70
|
+
if not document.get("content"):
|
|
71
|
+
return False
|
|
72
|
+
return _context_requires_document(
|
|
73
|
+
key.xid,
|
|
74
|
+
active_task,
|
|
75
|
+
active_workflow,
|
|
76
|
+
active_skill,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def mark_injected(
|
|
80
|
+
self,
|
|
81
|
+
document: dict[str, Any],
|
|
82
|
+
turn_id: str,
|
|
83
|
+
context_entry_id: str | None = None,
|
|
84
|
+
) -> XidContextEntry:
|
|
85
|
+
entry = self.register_materialized(document)
|
|
86
|
+
entry.visible_in_active_context = True
|
|
87
|
+
entry.injected_turns.append(str(turn_id))
|
|
88
|
+
entry.last_injected_turn = str(turn_id)
|
|
89
|
+
if context_entry_id is not None:
|
|
90
|
+
entry.context_entry_id = context_entry_id
|
|
91
|
+
elif entry.context_entry_id is None:
|
|
92
|
+
self._entry_sequence += 1
|
|
93
|
+
entry.context_entry_id = f"xid-context-{self._entry_sequence}"
|
|
94
|
+
return entry
|
|
95
|
+
|
|
96
|
+
def reference_existing(self, document: dict[str, Any], reason: str) -> dict[str, str]:
|
|
97
|
+
entry = self.register_materialized(document)
|
|
98
|
+
if reason not in entry.reuse_reasons:
|
|
99
|
+
entry.reuse_reasons.append(reason)
|
|
100
|
+
key = entry.key
|
|
101
|
+
result = {
|
|
102
|
+
"repository_fingerprint": key.repository_fingerprint,
|
|
103
|
+
"xid": key.xid,
|
|
104
|
+
"content_hash": key.content_hash,
|
|
105
|
+
"mode": "reuse_existing_session_context",
|
|
106
|
+
"reason": reason,
|
|
107
|
+
}
|
|
108
|
+
if entry.context_entry_id:
|
|
109
|
+
result["context_entry_id"] = entry.context_entry_id
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
def mark_context_compacted(
|
|
113
|
+
self,
|
|
114
|
+
documents: list[dict[str, Any]] | None = None,
|
|
115
|
+
) -> None:
|
|
116
|
+
if documents is None:
|
|
117
|
+
for entry in self._entries.values():
|
|
118
|
+
entry.visible_in_active_context = False
|
|
119
|
+
return
|
|
120
|
+
for document in documents:
|
|
121
|
+
entry = self._entries.get(XidContextKey.from_document(document))
|
|
122
|
+
if entry is not None:
|
|
123
|
+
entry.visible_in_active_context = False
|
|
124
|
+
|
|
125
|
+
def handle_version_change(
|
|
126
|
+
self,
|
|
127
|
+
document: dict[str, Any],
|
|
128
|
+
action: str = "replaced",
|
|
129
|
+
) -> list[dict[str, str]]:
|
|
130
|
+
key = XidContextKey.from_document(document)
|
|
131
|
+
changes: list[dict[str, str]] = []
|
|
132
|
+
for entry_key, entry in list(self._entries.items()):
|
|
133
|
+
if (
|
|
134
|
+
entry_key.repository_fingerprint == key.repository_fingerprint
|
|
135
|
+
and entry_key.xid == key.xid
|
|
136
|
+
and entry_key.content_hash != key.content_hash
|
|
137
|
+
and entry.visible_in_active_context
|
|
138
|
+
):
|
|
139
|
+
if action == "replaced":
|
|
140
|
+
entry.visible_in_active_context = False
|
|
141
|
+
change = {
|
|
142
|
+
"repository_fingerprint": key.repository_fingerprint,
|
|
143
|
+
"xid": key.xid,
|
|
144
|
+
"old_content_hash": entry_key.content_hash,
|
|
145
|
+
"new_content_hash": key.content_hash,
|
|
146
|
+
"action": action,
|
|
147
|
+
"reason": VERSION_CHANGE_REASON,
|
|
148
|
+
}
|
|
149
|
+
self.version_changes.append(change)
|
|
150
|
+
changes.append(change)
|
|
151
|
+
return changes
|
|
152
|
+
|
|
153
|
+
def entry_for(self, document: dict[str, Any]) -> XidContextEntry | None:
|
|
154
|
+
return self._entries.get(XidContextKey.from_document(document))
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class PromptContextAssembler:
|
|
158
|
+
def __init__(self, registry: SessionXidContextRegistry | None = None) -> None:
|
|
159
|
+
self.registry = registry or SessionXidContextRegistry()
|
|
160
|
+
|
|
161
|
+
def assemble(
|
|
162
|
+
self,
|
|
163
|
+
documents: list[dict[str, Any]],
|
|
164
|
+
*,
|
|
165
|
+
turn_id: str,
|
|
166
|
+
active_task: str,
|
|
167
|
+
active_workflow: dict[str, Any] | str | None = None,
|
|
168
|
+
active_skill: dict[str, Any] | str | None = None,
|
|
169
|
+
) -> dict[str, Any]:
|
|
170
|
+
prompt_items: list[dict[str, Any]] = []
|
|
171
|
+
trace = {
|
|
172
|
+
"turn_id": str(turn_id),
|
|
173
|
+
"active_task": active_task,
|
|
174
|
+
"injected_xids": [],
|
|
175
|
+
"reused_xids": [],
|
|
176
|
+
"version_changes": [],
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for document in documents:
|
|
180
|
+
self.registry.register_materialized(document)
|
|
181
|
+
visible_entry = self.registry.entry_for(document)
|
|
182
|
+
if visible_entry and visible_entry.visible_in_active_context:
|
|
183
|
+
reuse = self.registry.reference_existing(
|
|
184
|
+
document,
|
|
185
|
+
REUSE_SAME_VERSION_REASON,
|
|
186
|
+
)
|
|
187
|
+
prompt_items.append(reuse)
|
|
188
|
+
trace["reused_xids"].append(reuse)
|
|
189
|
+
continue
|
|
190
|
+
|
|
191
|
+
version_changes = self.registry.handle_version_change(document)
|
|
192
|
+
trace["version_changes"].extend(version_changes)
|
|
193
|
+
|
|
194
|
+
if self.registry.should_inject_body(
|
|
195
|
+
document,
|
|
196
|
+
active_task,
|
|
197
|
+
active_workflow,
|
|
198
|
+
active_skill,
|
|
199
|
+
):
|
|
200
|
+
entry = self.registry.mark_injected(document, str(turn_id))
|
|
201
|
+
item = _body_prompt_item(document, entry.context_entry_id)
|
|
202
|
+
prompt_items.append(item)
|
|
203
|
+
trace["injected_xids"].append(
|
|
204
|
+
{
|
|
205
|
+
"repository_fingerprint": document["repository_fingerprint"],
|
|
206
|
+
"xid": document["xid"],
|
|
207
|
+
"content_hash": document["content_hash"],
|
|
208
|
+
"reason": "body_required_by_active_context",
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
continue
|
|
212
|
+
|
|
213
|
+
prompt_items.append(_metadata_prompt_item(document))
|
|
214
|
+
|
|
215
|
+
return {"prompt_items": prompt_items, "trace": trace}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _metadata_prompt_item(document: dict[str, Any]) -> dict[str, Any]:
|
|
219
|
+
item = {
|
|
220
|
+
"mode": "metadata_only",
|
|
221
|
+
"xid": document.get("xid"),
|
|
222
|
+
"title": document.get("title"),
|
|
223
|
+
"summary": document.get("summary"),
|
|
224
|
+
"layer": document.get("layer"),
|
|
225
|
+
"required_at_init": document.get("required_at_init"),
|
|
226
|
+
"content_hash": document.get("content_hash"),
|
|
227
|
+
"links": document.get("links", []),
|
|
228
|
+
}
|
|
229
|
+
for field_name in ["cache_status", "client_cache_status"]:
|
|
230
|
+
if field_name in document:
|
|
231
|
+
item[field_name] = document[field_name]
|
|
232
|
+
return item
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _body_prompt_item(
|
|
236
|
+
document: dict[str, Any],
|
|
237
|
+
context_entry_id: str | None,
|
|
238
|
+
) -> dict[str, Any]:
|
|
239
|
+
item = _metadata_prompt_item(document)
|
|
240
|
+
item["mode"] = "body"
|
|
241
|
+
item["content"] = document["content"]
|
|
242
|
+
if context_entry_id:
|
|
243
|
+
item["context_entry_id"] = context_entry_id
|
|
244
|
+
return item
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _context_requires_document(
|
|
248
|
+
xid: str,
|
|
249
|
+
active_task: str,
|
|
250
|
+
active_workflow: dict[str, Any] | str | None,
|
|
251
|
+
active_skill: dict[str, Any] | str | None,
|
|
252
|
+
) -> bool:
|
|
253
|
+
task = active_task.lower()
|
|
254
|
+
if xid.lower() in task:
|
|
255
|
+
return True
|
|
256
|
+
return _context_declares_xid(active_workflow, xid) or _context_declares_xid(
|
|
257
|
+
active_skill,
|
|
258
|
+
xid,
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _context_declares_xid(context: dict[str, Any] | str | None, xid: str) -> bool:
|
|
263
|
+
if context is None:
|
|
264
|
+
return False
|
|
265
|
+
if isinstance(context, str):
|
|
266
|
+
return xid in context
|
|
267
|
+
return _contains_xid(context, xid)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _contains_xid(value: Any, xid: str) -> bool:
|
|
271
|
+
if isinstance(value, str):
|
|
272
|
+
return value == xid or xid in value
|
|
273
|
+
if isinstance(value, dict):
|
|
274
|
+
return any(_contains_xid(item, xid) for item in value.values())
|
|
275
|
+
if isinstance(value, list | tuple | set):
|
|
276
|
+
return any(_contains_xid(item, xid) for item in value)
|
|
277
|
+
return False
|
xrefkit/mcp/contracts.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .schemas import ToolContract
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def builtin_tool_contracts() -> list[ToolContract]:
|
|
7
|
+
contracts = [
|
|
8
|
+
ToolContract(
|
|
9
|
+
tool_id="xref.get_repository_identity",
|
|
10
|
+
provider="xrefkit-mcp",
|
|
11
|
+
version="1",
|
|
12
|
+
execution_location="server",
|
|
13
|
+
side_effects="none",
|
|
14
|
+
input_schema={},
|
|
15
|
+
output_schema={"identity": "repository_identity"},
|
|
16
|
+
requires_workspace=True,
|
|
17
|
+
required_when="A client must select an isolated cache namespace before loading cached XID documents.",
|
|
18
|
+
),
|
|
19
|
+
ToolContract(
|
|
20
|
+
tool_id="xref.get_startup_context",
|
|
21
|
+
provider="xrefkit-mcp",
|
|
22
|
+
version="2",
|
|
23
|
+
execution_location="server",
|
|
24
|
+
side_effects="none",
|
|
25
|
+
input_schema={"known_document_versions": "object?"},
|
|
26
|
+
output_schema={"context": "startup_context"},
|
|
27
|
+
requires_workspace=True,
|
|
28
|
+
required_when="Client initialization must show base XRefKit control documents and link-resolution instructions before task routing.",
|
|
29
|
+
),
|
|
30
|
+
ToolContract(
|
|
31
|
+
tool_id="xref.bind_skill_run",
|
|
32
|
+
provider="xrefkit-mcp",
|
|
33
|
+
version="1",
|
|
34
|
+
execution_location="server",
|
|
35
|
+
side_effects="audit_write",
|
|
36
|
+
input_schema={"run_id": "uuid", "skill_id": "string"},
|
|
37
|
+
output_schema={"binding": "skill_run_mcp_binding"},
|
|
38
|
+
requires_workspace=True,
|
|
39
|
+
required_when="A client created a Skill Run and must correlate subsequent MCP routing and XID access with that run.",
|
|
40
|
+
),
|
|
41
|
+
ToolContract(
|
|
42
|
+
tool_id="xref.end_skill_run",
|
|
43
|
+
provider="xrefkit-mcp",
|
|
44
|
+
version="1",
|
|
45
|
+
execution_location="server",
|
|
46
|
+
side_effects="audit_write",
|
|
47
|
+
input_schema={"run_id": "uuid"},
|
|
48
|
+
output_schema={"binding": "ended_skill_run_mcp_binding"},
|
|
49
|
+
requires_workspace=True,
|
|
50
|
+
required_when="A client completed a Skill Run and must stop attributing later MCP queries to it.",
|
|
51
|
+
),
|
|
52
|
+
ToolContract(
|
|
53
|
+
tool_id="xref.list_knowledge_catalog",
|
|
54
|
+
provider="xrefkit-mcp",
|
|
55
|
+
version="1",
|
|
56
|
+
execution_location="server",
|
|
57
|
+
side_effects="none",
|
|
58
|
+
input_schema={"limit": "integer?"},
|
|
59
|
+
output_schema={"entries": "knowledge_catalog_entries"},
|
|
60
|
+
requires_workspace=True,
|
|
61
|
+
required_when="Catalog browsing is needed without expanding knowledge body.",
|
|
62
|
+
response_envelope="mcp_result_array",
|
|
63
|
+
),
|
|
64
|
+
ToolContract(
|
|
65
|
+
tool_id="xref.search_knowledge_catalog",
|
|
66
|
+
provider="xrefkit-mcp",
|
|
67
|
+
version="1",
|
|
68
|
+
execution_location="server",
|
|
69
|
+
side_effects="audit_write",
|
|
70
|
+
input_schema={"query": "string", "limit": "integer?"},
|
|
71
|
+
output_schema={"entries": "knowledge_catalog_entries"},
|
|
72
|
+
requires_workspace=True,
|
|
73
|
+
required_when="A purpose or question must be matched to XID-backed knowledge.",
|
|
74
|
+
response_envelope="mcp_result_array",
|
|
75
|
+
),
|
|
76
|
+
ToolContract(
|
|
77
|
+
tool_id="xref.expand_knowledge",
|
|
78
|
+
provider="xrefkit-mcp",
|
|
79
|
+
version="1",
|
|
80
|
+
execution_location="server",
|
|
81
|
+
side_effects="audit_write",
|
|
82
|
+
input_schema={"xid": "string"},
|
|
83
|
+
output_schema={"entry": "knowledge_catalog_entry", "content": "markdown"},
|
|
84
|
+
requires_workspace=True,
|
|
85
|
+
required_when="The consumer explicitly needs the selected knowledge body.",
|
|
86
|
+
),
|
|
87
|
+
ToolContract(
|
|
88
|
+
tool_id="xref.get_knowledge_summary",
|
|
89
|
+
provider="xrefkit-mcp",
|
|
90
|
+
version="1",
|
|
91
|
+
execution_location="server",
|
|
92
|
+
side_effects="audit_write",
|
|
93
|
+
input_schema={"xid": "string"},
|
|
94
|
+
output_schema={"entry": "knowledge_catalog_entry"},
|
|
95
|
+
requires_workspace=True,
|
|
96
|
+
required_when="The consumer needs metadata for one Knowledge entry without expanding the body.",
|
|
97
|
+
),
|
|
98
|
+
ToolContract(
|
|
99
|
+
tool_id="xref.get_document_by_xid",
|
|
100
|
+
provider="xrefkit-mcp",
|
|
101
|
+
version="2",
|
|
102
|
+
execution_location="server",
|
|
103
|
+
side_effects="audit_write",
|
|
104
|
+
input_schema={"xid": "string", "known_version": "string?"},
|
|
105
|
+
output_schema={"document": "conditional_xref_document"},
|
|
106
|
+
requires_workspace=True,
|
|
107
|
+
required_when="Transferred content has a links[] entry whose xid must be resolved because the client does not have local Markdown files.",
|
|
108
|
+
),
|
|
109
|
+
ToolContract(
|
|
110
|
+
tool_id="xref.build_knowledge_context",
|
|
111
|
+
provider="xrefkit-mcp",
|
|
112
|
+
version="1",
|
|
113
|
+
execution_location="server",
|
|
114
|
+
side_effects="audit_write",
|
|
115
|
+
input_schema={"query": "string", "limit": "integer?"},
|
|
116
|
+
output_schema={"entries": "expanded_knowledge", "missing": "array"},
|
|
117
|
+
requires_workspace=True,
|
|
118
|
+
required_when="A bounded context pack is needed for a task.",
|
|
119
|
+
),
|
|
120
|
+
ToolContract(
|
|
121
|
+
tool_id="xref.list_skills",
|
|
122
|
+
provider="xrefkit-mcp",
|
|
123
|
+
version="3",
|
|
124
|
+
execution_location="server",
|
|
125
|
+
side_effects="none",
|
|
126
|
+
input_schema={"limit": "integer?", "include_content": "boolean?"},
|
|
127
|
+
output_schema={"entries": "skill_catalog_entries"},
|
|
128
|
+
requires_workspace=True,
|
|
129
|
+
required_when=(
|
|
130
|
+
"Available Skills must be listed without executing them. "
|
|
131
|
+
"Metadata-only by default; include_content=true returns "
|
|
132
|
+
"procedure bodies and requires get_startup_context first."
|
|
133
|
+
),
|
|
134
|
+
response_envelope="mcp_result_array",
|
|
135
|
+
),
|
|
136
|
+
ToolContract(
|
|
137
|
+
tool_id="xref.get_skill",
|
|
138
|
+
provider="xrefkit-mcp",
|
|
139
|
+
version="2",
|
|
140
|
+
execution_location="server",
|
|
141
|
+
side_effects="audit_write",
|
|
142
|
+
input_schema={
|
|
143
|
+
"skill_id": "string",
|
|
144
|
+
"known_document_versions": "object?",
|
|
145
|
+
},
|
|
146
|
+
output_schema={"entry": "skill_catalog_entry"},
|
|
147
|
+
requires_workspace=True,
|
|
148
|
+
required_when="The client needs a Skill's meta/procedure content and skill_links that are resolved through get_document_by_xid.",
|
|
149
|
+
),
|
|
150
|
+
ToolContract(
|
|
151
|
+
tool_id="xref.get_skill_requirements",
|
|
152
|
+
provider="xrefkit-mcp",
|
|
153
|
+
version="1",
|
|
154
|
+
execution_location="server",
|
|
155
|
+
side_effects="none",
|
|
156
|
+
input_schema={"skill_id": "string"},
|
|
157
|
+
output_schema={"requirements": "skill_requirements"},
|
|
158
|
+
requires_workspace=True,
|
|
159
|
+
required_when="The client needs a Skill's required Knowledge, required tools, and closure contract without loading full Skill bodies.",
|
|
160
|
+
),
|
|
161
|
+
ToolContract(
|
|
162
|
+
tool_id="xref.rank_skills_for_purpose",
|
|
163
|
+
provider="xrefkit-mcp",
|
|
164
|
+
version="1",
|
|
165
|
+
execution_location="server",
|
|
166
|
+
side_effects="audit_write",
|
|
167
|
+
input_schema={"purpose": "string", "limit": "integer?"},
|
|
168
|
+
output_schema={"results": "skill_rank_results"},
|
|
169
|
+
requires_workspace=True,
|
|
170
|
+
required_when="The consumer needs candidate Skills with evidence, not an automatic decision.",
|
|
171
|
+
response_envelope="mcp_result_array",
|
|
172
|
+
),
|
|
173
|
+
ToolContract(
|
|
174
|
+
tool_id="xref.list_tool_contracts",
|
|
175
|
+
provider="xrefkit-mcp",
|
|
176
|
+
version="1",
|
|
177
|
+
execution_location="server",
|
|
178
|
+
side_effects="none",
|
|
179
|
+
input_schema={},
|
|
180
|
+
output_schema={"contracts": "tool_contracts"},
|
|
181
|
+
requires_workspace=False,
|
|
182
|
+
required_when="The client performs capability handshake or audit.",
|
|
183
|
+
response_envelope="mcp_result_array",
|
|
184
|
+
),
|
|
185
|
+
ToolContract(
|
|
186
|
+
tool_id="xref.get_client_tool_manifest",
|
|
187
|
+
provider="xrefkit-mcp",
|
|
188
|
+
version="1",
|
|
189
|
+
execution_location="server",
|
|
190
|
+
side_effects="none",
|
|
191
|
+
input_schema={},
|
|
192
|
+
output_schema={"distribution": "client_tool_distribution"},
|
|
193
|
+
requires_workspace=True,
|
|
194
|
+
required_when="The client needs to know which Python tools can be installed for client-side execution.",
|
|
195
|
+
),
|
|
196
|
+
ToolContract(
|
|
197
|
+
tool_id="xref.get_client_tool_file",
|
|
198
|
+
provider="xrefkit-mcp",
|
|
199
|
+
version="1",
|
|
200
|
+
execution_location="server",
|
|
201
|
+
side_effects="none",
|
|
202
|
+
input_schema={"path": "string"},
|
|
203
|
+
output_schema={"file": "client_tool_file"},
|
|
204
|
+
requires_workspace=True,
|
|
205
|
+
required_when="The client needs one distributed tool file content by manifest path.",
|
|
206
|
+
),
|
|
207
|
+
ToolContract(
|
|
208
|
+
tool_id="xref.get_client_tool_bundle",
|
|
209
|
+
provider="xrefkit-mcp",
|
|
210
|
+
version="1",
|
|
211
|
+
execution_location="server",
|
|
212
|
+
side_effects="none",
|
|
213
|
+
input_schema={},
|
|
214
|
+
output_schema={"distribution": "client_tool_distribution", "files": "client_tool_files"},
|
|
215
|
+
requires_workspace=True,
|
|
216
|
+
required_when="The client needs to install all distributable Python tools for client-side execution.",
|
|
217
|
+
),
|
|
218
|
+
ToolContract(
|
|
219
|
+
tool_id="xref.get_client_tool_pip_package",
|
|
220
|
+
provider="xrefkit-mcp",
|
|
221
|
+
version="1",
|
|
222
|
+
execution_location="server",
|
|
223
|
+
side_effects="none",
|
|
224
|
+
input_schema={},
|
|
225
|
+
output_schema={"package": "client_tool_pip_package"},
|
|
226
|
+
requires_workspace=True,
|
|
227
|
+
required_when="The client wants a pip-installable zip package for client-side XRefKit Python tools.",
|
|
228
|
+
),
|
|
229
|
+
ToolContract(
|
|
230
|
+
tool_id="xref.check_client_tool_versions",
|
|
231
|
+
provider="xrefkit-mcp",
|
|
232
|
+
version="1",
|
|
233
|
+
execution_location="server",
|
|
234
|
+
side_effects="none",
|
|
235
|
+
input_schema={"installed": "object"},
|
|
236
|
+
output_schema={"result": "client_tool_version_check"},
|
|
237
|
+
requires_workspace=True,
|
|
238
|
+
required_when="Client initialization must compare installed client-side tool package versions with the server manifest.",
|
|
239
|
+
),
|
|
240
|
+
]
|
|
241
|
+
for contract in contracts:
|
|
242
|
+
contract.validate()
|
|
243
|
+
return contracts
|