@contractspec/bundle.library 3.8.2 → 3.8.5
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.
- package/.turbo/turbo-build.log +130 -116
- package/CHANGELOG.md +80 -0
- package/dist/application/index.js +806 -131
- package/dist/application/mcp/cliMcp.js +21 -2
- package/dist/application/mcp/common.js +21 -2
- package/dist/application/mcp/common.test.d.ts +1 -0
- package/dist/application/mcp/contractsMcp.js +21 -2
- package/dist/application/mcp/docsMcp.catalog.d.ts +2 -0
- package/dist/application/mcp/docsMcp.catalog.js +382 -0
- package/dist/application/mcp/docsMcp.d.ts +5 -1
- package/dist/application/mcp/docsMcp.data.d.ts +85 -0
- package/dist/application/mcp/docsMcp.data.js +148 -0
- package/dist/application/mcp/docsMcp.js +776 -101
- package/dist/application/mcp/docsMcp.prompts.d.ts +3 -0
- package/dist/application/mcp/docsMcp.prompts.js +522 -0
- package/dist/application/mcp/docsMcp.reference.d.ts +24 -0
- package/dist/application/mcp/docsMcp.reference.js +236 -0
- package/dist/application/mcp/docsMcp.resources.d.ts +3 -0
- package/dist/application/mcp/docsMcp.resources.js +520 -0
- package/dist/application/mcp/docsMcp.test.d.ts +1 -0
- package/dist/application/mcp/docsMcp.tools.d.ts +3 -0
- package/dist/application/mcp/docsMcp.tools.js +519 -0
- package/dist/application/mcp/index.js +806 -131
- package/dist/application/mcp/internalMcp.js +21 -2
- package/dist/application/mcp/normalizeMcpRequest.d.ts +1 -0
- package/dist/application/mcp/normalizeMcpRequest.js +22 -0
- package/dist/application/mcp/providerRankingMcp.js +21 -2
- package/dist/components/docs/generated/docs-index.generated.d.ts +6 -6
- package/dist/components/docs/index.js +23 -24
- package/dist/components/docs/libraries/LibrariesAiAgentPage.js +22 -22
- package/dist/components/docs/libraries/LibrariesSupportBotPage.js +1 -2
- package/dist/components/docs/libraries/index.js +23 -24
- package/dist/components/templates/engine/index.d.ts +1 -1
- package/dist/components/templates/engine/index.js +1 -1
- package/dist/features/index.js +15 -15
- package/dist/index.js +194 -195
- package/dist/node/application/index.js +806 -131
- package/dist/node/application/mcp/cliMcp.js +21 -2
- package/dist/node/application/mcp/common.js +21 -2
- package/dist/node/application/mcp/contractsMcp.js +21 -2
- package/dist/node/application/mcp/docsMcp.catalog.js +381 -0
- package/dist/node/application/mcp/docsMcp.data.js +147 -0
- package/dist/node/application/mcp/docsMcp.js +776 -101
- package/dist/node/application/mcp/docsMcp.prompts.js +521 -0
- package/dist/node/application/mcp/docsMcp.reference.js +235 -0
- package/dist/node/application/mcp/docsMcp.resources.js +519 -0
- package/dist/node/application/mcp/docsMcp.tools.js +518 -0
- package/dist/node/application/mcp/index.js +806 -131
- package/dist/node/application/mcp/internalMcp.js +21 -2
- package/dist/node/application/mcp/normalizeMcpRequest.js +21 -0
- package/dist/node/application/mcp/providerRankingMcp.js +21 -2
- package/dist/node/components/docs/index.js +23 -24
- package/dist/node/components/docs/libraries/LibrariesAiAgentPage.js +22 -22
- package/dist/node/components/docs/libraries/LibrariesSupportBotPage.js +1 -2
- package/dist/node/components/docs/libraries/index.js +23 -24
- package/dist/node/components/templates/engine/index.js +1 -1
- package/dist/node/features/index.js +15 -15
- package/dist/node/index.js +194 -195
- package/dist/node/presentation/features/hooks/index.js +12 -12
- package/dist/node/presentation/features/hooks/useContractsRegistry.js +12 -12
- package/dist/node/presentation/features/index.js +12 -12
- package/dist/node/presentation/features/organisms/FeatureDataViewsList.js +12 -12
- package/dist/node/presentation/features/organisms/FeatureEventsList.js +12 -12
- package/dist/node/presentation/features/organisms/FeatureFormsList.js +12 -12
- package/dist/node/presentation/features/organisms/FeaturePresentationsList.js +12 -12
- package/dist/node/presentation/features/organisms/index.js +12 -12
- package/dist/node/presentation/features/templates/FeatureDataViewsTemplate/FeatureDataViewsTemplate.js +12 -12
- package/dist/node/presentation/features/templates/FeatureDataViewsTemplate/index.js +12 -12
- package/dist/node/presentation/features/templates/FeatureEventsTemplate/FeatureEventsTemplate.js +12 -12
- package/dist/node/presentation/features/templates/FeatureEventsTemplate/index.js +12 -12
- package/dist/node/presentation/features/templates/FeatureFormsTemplate/FeatureFormsTemplate.js +12 -12
- package/dist/node/presentation/features/templates/FeatureFormsTemplate/index.js +12 -12
- package/dist/node/presentation/features/templates/FeaturePresentationsTemplate/FeaturePresentationsTemplate.js +12 -12
- package/dist/node/presentation/features/templates/FeaturePresentationsTemplate/index.js +12 -12
- package/dist/presentation/features/hooks/index.js +12 -12
- package/dist/presentation/features/hooks/useContractsRegistry.js +12 -12
- package/dist/presentation/features/index.js +12 -12
- package/dist/presentation/features/organisms/FeatureDataViewsList.js +12 -12
- package/dist/presentation/features/organisms/FeatureEventsList.js +12 -12
- package/dist/presentation/features/organisms/FeatureFormsList.js +12 -12
- package/dist/presentation/features/organisms/FeaturePresentationsList.js +12 -12
- package/dist/presentation/features/organisms/index.js +12 -12
- package/dist/presentation/features/templates/FeatureDataViewsTemplate/FeatureDataViewsTemplate.js +12 -12
- package/dist/presentation/features/templates/FeatureDataViewsTemplate/index.js +12 -12
- package/dist/presentation/features/templates/FeatureEventsTemplate/FeatureEventsTemplate.js +12 -12
- package/dist/presentation/features/templates/FeatureEventsTemplate/index.js +12 -12
- package/dist/presentation/features/templates/FeatureFormsTemplate/FeatureFormsTemplate.js +12 -12
- package/dist/presentation/features/templates/FeatureFormsTemplate/index.js +12 -12
- package/dist/presentation/features/templates/FeaturePresentationsTemplate/FeaturePresentationsTemplate.js +12 -12
- package/dist/presentation/features/templates/FeaturePresentationsTemplate/index.js +12 -12
- package/package.json +108 -23
- package/src/application/mcp/common.test.ts +64 -0
- package/src/application/mcp/common.ts +5 -2
- package/src/application/mcp/docsMcp.catalog.ts +2 -0
- package/src/application/mcp/docsMcp.data.ts +196 -0
- package/src/application/mcp/docsMcp.prompts.ts +165 -0
- package/src/application/mcp/docsMcp.reference.ts +152 -0
- package/src/application/mcp/docsMcp.resources.ts +194 -0
- package/src/application/mcp/docsMcp.test.ts +148 -0
- package/src/application/mcp/docsMcp.tools.ts +183 -0
- package/src/application/mcp/docsMcp.ts +13 -177
- package/src/application/mcp/normalizeMcpRequest.ts +30 -0
- package/src/components/docs/generated/docs-index._common.json +1 -1
- package/src/components/docs/generated/docs-index.ai-chat-assistant.json +8 -0
- package/src/components/docs/generated/docs-index.artisan-knowledge-product.json +18 -0
- package/src/components/docs/generated/docs-index.artisan-payments-stripe.json +34 -0
- package/src/components/docs/generated/docs-index.data-grid-showcase.json +18 -0
- package/src/components/docs/generated/docs-index.generated.ts +6 -6
- package/src/components/docs/generated/docs-index.harness-lab.json +10 -0
- package/src/components/docs/generated/docs-index.locale-jurisdiction-gate.json +8 -0
- package/src/components/docs/generated/docs-index.manifest.json +50 -5
- package/src/components/docs/generated/docs-index.messaging-agent-actions.json +18 -0
- package/src/components/docs/generated/docs-index.mobile-demo-tasks.json +34 -0
- package/src/components/docs/generated/docs-index.opencode-cli.json +2 -2
- package/src/components/docs/generated/docs-index.platform-control-plane.json +250 -0
- package/src/components/docs/generated/docs-index.platform-harness.json +178 -0
- package/src/components/docs/generated/docs-index.platform-integrations.json +22 -14
- package/src/components/docs/generated/docs-index.visualization-showcase.json +18 -0
- package/src/components/docs/libraries/LibrariesAiAgentPage.tsx +20 -17
- package/src/components/docs/libraries/LibrariesSupportBotPage.tsx +1 -2
- package/src/components/templates/engine/index.ts +2 -2
|
@@ -25,6 +25,25 @@ var authLogger = new Logger({
|
|
|
25
25
|
enableContext: true,
|
|
26
26
|
enableColors: false
|
|
27
27
|
});
|
|
28
|
+
// src/application/mcp/normalizeMcpRequest.ts
|
|
29
|
+
var REQUIRED_ACCEPT_TYPES = ["application/json", "text/event-stream"];
|
|
30
|
+
function canNormalizeAcceptHeader(acceptHeader) {
|
|
31
|
+
return !acceptHeader || acceptHeader.includes("*/*") || acceptHeader.includes("application/*") || REQUIRED_ACCEPT_TYPES.some((value) => acceptHeader.includes(value));
|
|
32
|
+
}
|
|
33
|
+
function normalizeMcpRequest(request) {
|
|
34
|
+
if (request.method !== "POST")
|
|
35
|
+
return request;
|
|
36
|
+
const acceptHeader = request.headers.get("accept");
|
|
37
|
+
if (!canNormalizeAcceptHeader(acceptHeader))
|
|
38
|
+
return request;
|
|
39
|
+
const missingTypes = REQUIRED_ACCEPT_TYPES.filter((value) => !acceptHeader?.includes(value));
|
|
40
|
+
if (missingTypes.length === 0)
|
|
41
|
+
return request;
|
|
42
|
+
const headers = new Headers(request.headers);
|
|
43
|
+
headers.set("accept", [acceptHeader, ...missingTypes].filter(Boolean).join(", "));
|
|
44
|
+
return new Request(request, { headers });
|
|
45
|
+
}
|
|
46
|
+
|
|
28
47
|
// src/application/mcp/common.ts
|
|
29
48
|
import { randomUUID } from "node:crypto";
|
|
30
49
|
import { createMcpServer } from "@contractspec/lib.contracts-runtime-server-mcp/provider-mcp";
|
|
@@ -120,7 +139,7 @@ function createMcpElysiaHandler({
|
|
|
120
139
|
stateful: false
|
|
121
140
|
});
|
|
122
141
|
try {
|
|
123
|
-
return await state.transport.handleRequest(request);
|
|
142
|
+
return await state.transport.handleRequest(normalizeMcpRequest(request));
|
|
124
143
|
} finally {
|
|
125
144
|
await closeSessionState(state);
|
|
126
145
|
}
|
|
@@ -156,7 +175,7 @@ function createMcpElysiaHandler({
|
|
|
156
175
|
createdState = true;
|
|
157
176
|
}
|
|
158
177
|
try {
|
|
159
|
-
const response = await state.transport.handleRequest(request);
|
|
178
|
+
const response = await state.transport.handleRequest(normalizeMcpRequest(request));
|
|
160
179
|
const activeSessionId = state.transport.sessionId;
|
|
161
180
|
if (activeSessionId && !sessions.has(activeSessionId)) {
|
|
162
181
|
sessions.set(activeSessionId, state);
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
// src/application/mcp/normalizeMcpRequest.ts
|
|
2
|
+
var REQUIRED_ACCEPT_TYPES = ["application/json", "text/event-stream"];
|
|
3
|
+
function canNormalizeAcceptHeader(acceptHeader) {
|
|
4
|
+
return !acceptHeader || acceptHeader.includes("*/*") || acceptHeader.includes("application/*") || REQUIRED_ACCEPT_TYPES.some((value) => acceptHeader.includes(value));
|
|
5
|
+
}
|
|
6
|
+
function normalizeMcpRequest(request) {
|
|
7
|
+
if (request.method !== "POST")
|
|
8
|
+
return request;
|
|
9
|
+
const acceptHeader = request.headers.get("accept");
|
|
10
|
+
if (!canNormalizeAcceptHeader(acceptHeader))
|
|
11
|
+
return request;
|
|
12
|
+
const missingTypes = REQUIRED_ACCEPT_TYPES.filter((value) => !acceptHeader?.includes(value));
|
|
13
|
+
if (missingTypes.length === 0)
|
|
14
|
+
return request;
|
|
15
|
+
const headers = new Headers(request.headers);
|
|
16
|
+
headers.set("accept", [acceptHeader, ...missingTypes].filter(Boolean).join(", "));
|
|
17
|
+
return new Request(request, { headers });
|
|
18
|
+
}
|
|
19
|
+
|
|
1
20
|
// src/application/mcp/common.ts
|
|
2
21
|
import { randomUUID } from "node:crypto";
|
|
3
22
|
import { createMcpServer } from "@contractspec/lib.contracts-runtime-server-mcp/provider-mcp";
|
|
@@ -93,7 +112,7 @@ function createMcpElysiaHandler({
|
|
|
93
112
|
stateful: false
|
|
94
113
|
});
|
|
95
114
|
try {
|
|
96
|
-
return await state.transport.handleRequest(request);
|
|
115
|
+
return await state.transport.handleRequest(normalizeMcpRequest(request));
|
|
97
116
|
} finally {
|
|
98
117
|
await closeSessionState(state);
|
|
99
118
|
}
|
|
@@ -129,7 +148,7 @@ function createMcpElysiaHandler({
|
|
|
129
148
|
createdState = true;
|
|
130
149
|
}
|
|
131
150
|
try {
|
|
132
|
-
const response = await state.transport.handleRequest(request);
|
|
151
|
+
const response = await state.transport.handleRequest(normalizeMcpRequest(request));
|
|
133
152
|
const activeSessionId = state.transport.sessionId;
|
|
134
153
|
if (activeSessionId && !sessions.has(activeSessionId)) {
|
|
135
154
|
sessions.set(activeSessionId, state);
|
|
@@ -25,6 +25,25 @@ var authLogger = new Logger({
|
|
|
25
25
|
enableContext: true,
|
|
26
26
|
enableColors: false
|
|
27
27
|
});
|
|
28
|
+
// src/application/mcp/normalizeMcpRequest.ts
|
|
29
|
+
var REQUIRED_ACCEPT_TYPES = ["application/json", "text/event-stream"];
|
|
30
|
+
function canNormalizeAcceptHeader(acceptHeader) {
|
|
31
|
+
return !acceptHeader || acceptHeader.includes("*/*") || acceptHeader.includes("application/*") || REQUIRED_ACCEPT_TYPES.some((value) => acceptHeader.includes(value));
|
|
32
|
+
}
|
|
33
|
+
function normalizeMcpRequest(request) {
|
|
34
|
+
if (request.method !== "POST")
|
|
35
|
+
return request;
|
|
36
|
+
const acceptHeader = request.headers.get("accept");
|
|
37
|
+
if (!canNormalizeAcceptHeader(acceptHeader))
|
|
38
|
+
return request;
|
|
39
|
+
const missingTypes = REQUIRED_ACCEPT_TYPES.filter((value) => !acceptHeader?.includes(value));
|
|
40
|
+
if (missingTypes.length === 0)
|
|
41
|
+
return request;
|
|
42
|
+
const headers = new Headers(request.headers);
|
|
43
|
+
headers.set("accept", [acceptHeader, ...missingTypes].filter(Boolean).join(", "));
|
|
44
|
+
return new Request(request, { headers });
|
|
45
|
+
}
|
|
46
|
+
|
|
28
47
|
// src/application/mcp/common.ts
|
|
29
48
|
import { randomUUID } from "node:crypto";
|
|
30
49
|
import { createMcpServer } from "@contractspec/lib.contracts-runtime-server-mcp/provider-mcp";
|
|
@@ -120,7 +139,7 @@ function createMcpElysiaHandler({
|
|
|
120
139
|
stateful: false
|
|
121
140
|
});
|
|
122
141
|
try {
|
|
123
|
-
return await state.transport.handleRequest(request);
|
|
142
|
+
return await state.transport.handleRequest(normalizeMcpRequest(request));
|
|
124
143
|
} finally {
|
|
125
144
|
await closeSessionState(state);
|
|
126
145
|
}
|
|
@@ -156,7 +175,7 @@ function createMcpElysiaHandler({
|
|
|
156
175
|
createdState = true;
|
|
157
176
|
}
|
|
158
177
|
try {
|
|
159
|
-
const response = await state.transport.handleRequest(request);
|
|
178
|
+
const response = await state.transport.handleRequest(normalizeMcpRequest(request));
|
|
160
179
|
const activeSessionId = state.transport.sessionId;
|
|
161
180
|
if (activeSessionId && !sessions.has(activeSessionId)) {
|
|
162
181
|
sessions.set(activeSessionId, state);
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
// src/application/mcp/docsMcp.data.ts
|
|
2
|
+
import { defaultDocRegistry } from "@contractspec/lib.contracts-spec/docs";
|
|
3
|
+
var DEFAULT_LIMIT = 20;
|
|
4
|
+
var MAX_LIMIT = 100;
|
|
5
|
+
function normalizeText(value) {
|
|
6
|
+
return value?.trim().toLowerCase() ?? "";
|
|
7
|
+
}
|
|
8
|
+
function normalizeRoute(route) {
|
|
9
|
+
const decoded = decodeURIComponent(route).trim();
|
|
10
|
+
if (!decoded)
|
|
11
|
+
return "/";
|
|
12
|
+
return decoded.startsWith("/") ? decoded : `/${decoded}`;
|
|
13
|
+
}
|
|
14
|
+
function normalizeTags(value) {
|
|
15
|
+
const tags = Array.isArray(value) ? value : value ? [value] : [];
|
|
16
|
+
return tags.map((tag) => normalizeText(tag)).filter(Boolean);
|
|
17
|
+
}
|
|
18
|
+
function clampLimit(limit) {
|
|
19
|
+
if (!limit || Number.isNaN(limit))
|
|
20
|
+
return DEFAULT_LIMIT;
|
|
21
|
+
return Math.min(Math.max(limit, 1), MAX_LIMIT);
|
|
22
|
+
}
|
|
23
|
+
function clampOffset(offset) {
|
|
24
|
+
if (!offset || Number.isNaN(offset))
|
|
25
|
+
return 0;
|
|
26
|
+
return Math.max(offset, 0);
|
|
27
|
+
}
|
|
28
|
+
function toDocSummary({ block, route }) {
|
|
29
|
+
return {
|
|
30
|
+
id: block.id,
|
|
31
|
+
title: block.title,
|
|
32
|
+
summary: block.summary ?? "",
|
|
33
|
+
route,
|
|
34
|
+
visibility: block.visibility ?? "public",
|
|
35
|
+
kind: block.kind ?? "reference",
|
|
36
|
+
version: block.version ?? "1.0.0",
|
|
37
|
+
tags: block.tags ?? []
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function scoreDoc(route, query) {
|
|
41
|
+
if (!query)
|
|
42
|
+
return 1;
|
|
43
|
+
const tokens = query.split(/\s+/).filter(Boolean);
|
|
44
|
+
const title = normalizeText(route.block.title);
|
|
45
|
+
const id = normalizeText(route.block.id);
|
|
46
|
+
const summary = normalizeText(route.block.summary);
|
|
47
|
+
const body = normalizeText(route.block.body);
|
|
48
|
+
const path = normalizeText(route.route);
|
|
49
|
+
const tags = (route.block.tags ?? []).map((tag) => normalizeText(tag));
|
|
50
|
+
const haystack = [title, id, summary, body, path, ...tags].join(" ");
|
|
51
|
+
if (tokens.some((token) => !haystack.includes(token)))
|
|
52
|
+
return 0;
|
|
53
|
+
let score = 0;
|
|
54
|
+
for (const token of tokens) {
|
|
55
|
+
if (id.includes(token))
|
|
56
|
+
score += 8;
|
|
57
|
+
if (title.includes(token))
|
|
58
|
+
score += 7;
|
|
59
|
+
if (tags.some((tag) => tag.includes(token)))
|
|
60
|
+
score += 5;
|
|
61
|
+
if (summary.includes(token))
|
|
62
|
+
score += 4;
|
|
63
|
+
if (path.includes(token))
|
|
64
|
+
score += 3;
|
|
65
|
+
if (body.includes(token))
|
|
66
|
+
score += 2;
|
|
67
|
+
}
|
|
68
|
+
return score;
|
|
69
|
+
}
|
|
70
|
+
function searchDocs(routes, args) {
|
|
71
|
+
const query = normalizeText(typeof args.query === "string" ? args.query : undefined);
|
|
72
|
+
const tags = normalizeTags(args.tag);
|
|
73
|
+
const visibility = normalizeText(typeof args.visibility === "string" ? args.visibility : undefined);
|
|
74
|
+
const kind = normalizeText(typeof args.kind === "string" ? args.kind : undefined);
|
|
75
|
+
const limit = clampLimit(typeof args.limit === "number" ? args.limit : undefined);
|
|
76
|
+
const offset = clampOffset(typeof args.offset === "number" ? args.offset : undefined);
|
|
77
|
+
const ranked = routes.map((route) => ({
|
|
78
|
+
doc: toDocSummary(route),
|
|
79
|
+
score: scoreDoc(route, query)
|
|
80
|
+
})).filter(({ doc, score }) => {
|
|
81
|
+
const matchesQuery = query ? score > 0 : true;
|
|
82
|
+
const matchesTags = tags.length ? tags.every((tag) => doc.tags.some((docTag) => normalizeText(docTag).includes(tag))) : true;
|
|
83
|
+
const matchesVisibility = visibility ? normalizeText(doc.visibility) === visibility : true;
|
|
84
|
+
const matchesKind = kind ? normalizeText(doc.kind) === kind : true;
|
|
85
|
+
return matchesQuery && matchesTags && matchesVisibility && matchesKind;
|
|
86
|
+
}).sort((left, right) => {
|
|
87
|
+
if (right.score !== left.score)
|
|
88
|
+
return right.score - left.score;
|
|
89
|
+
return left.doc.title.localeCompare(right.doc.title);
|
|
90
|
+
});
|
|
91
|
+
const docs = ranked.slice(offset, offset + limit).map(({ doc }) => doc);
|
|
92
|
+
const nextOffset = offset + docs.length < ranked.length ? offset + docs.length : undefined;
|
|
93
|
+
return {
|
|
94
|
+
docs,
|
|
95
|
+
items: docs,
|
|
96
|
+
total: ranked.length,
|
|
97
|
+
...nextOffset != null ? { nextOffset } : {}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function getDocById(id) {
|
|
101
|
+
const normalizedId = decodeURIComponent(id);
|
|
102
|
+
const found = defaultDocRegistry.get(normalizedId);
|
|
103
|
+
if (!found)
|
|
104
|
+
return;
|
|
105
|
+
return {
|
|
106
|
+
doc: toDocSummary(found),
|
|
107
|
+
content: String(found.block.body ?? "")
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function getDocByRoute(routes, routePath) {
|
|
111
|
+
const normalizedPath = normalizeRoute(routePath);
|
|
112
|
+
const found = routes.find((route) => normalizeRoute(route.route) === normalizedPath);
|
|
113
|
+
if (!found)
|
|
114
|
+
return;
|
|
115
|
+
return {
|
|
116
|
+
doc: toDocSummary(found),
|
|
117
|
+
content: String(found.block.body ?? "")
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function listDocFacets(routes) {
|
|
121
|
+
const tags = new Map;
|
|
122
|
+
const kinds = new Map;
|
|
123
|
+
const visibilities = new Map;
|
|
124
|
+
for (const route of routes) {
|
|
125
|
+
const kind = route.block.kind ?? "reference";
|
|
126
|
+
const visibility = route.block.visibility ?? "public";
|
|
127
|
+
kinds.set(kind, (kinds.get(kind) ?? 0) + 1);
|
|
128
|
+
visibilities.set(visibility, (visibilities.get(visibility) ?? 0) + 1);
|
|
129
|
+
for (const tag of route.block.tags ?? []) {
|
|
130
|
+
tags.set(tag, (tags.get(tag) ?? 0) + 1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const toEntries = (values, key) => [...values.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])).map(([value, count]) => ({ [key]: value, count }));
|
|
134
|
+
return {
|
|
135
|
+
totalDocs: routes.length,
|
|
136
|
+
tags: toEntries(tags, "tag"),
|
|
137
|
+
kinds: toEntries(kinds, "kind"),
|
|
138
|
+
visibilities: toEntries(visibilities, "visibility")
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/features/contracts-registry.ts
|
|
143
|
+
import {
|
|
144
|
+
EventRegistry,
|
|
145
|
+
OperationSpecRegistry
|
|
146
|
+
} from "@contractspec/lib.contracts-spec";
|
|
147
|
+
import {
|
|
148
|
+
DataViewRegistry
|
|
149
|
+
} from "@contractspec/lib.contracts-spec/data-views";
|
|
150
|
+
import {
|
|
151
|
+
ContractReferenceDataView,
|
|
152
|
+
ContractReferenceQuery,
|
|
153
|
+
DocsGenerateCommand,
|
|
154
|
+
DocsGeneratedEvent,
|
|
155
|
+
DocsIndexDataView,
|
|
156
|
+
DocsIndexQuery,
|
|
157
|
+
DocsLayoutPresentation,
|
|
158
|
+
DocsPublishCommand,
|
|
159
|
+
DocsPublishedEvent,
|
|
160
|
+
DocsReferencePagePresentation,
|
|
161
|
+
DocsSearchForm,
|
|
162
|
+
ExampleCatalogDataView
|
|
163
|
+
} from "@contractspec/lib.contracts-spec/docs";
|
|
164
|
+
import { FormRegistry } from "@contractspec/lib.contracts-spec/forms";
|
|
165
|
+
import {
|
|
166
|
+
PresentationRegistry
|
|
167
|
+
} from "@contractspec/lib.contracts-spec/presentations";
|
|
168
|
+
import {
|
|
169
|
+
serializeDataViewSpec,
|
|
170
|
+
serializeEventSpec,
|
|
171
|
+
serializeFormSpec,
|
|
172
|
+
serializeOperationSpec,
|
|
173
|
+
serializePresentationSpec
|
|
174
|
+
} from "@contractspec/lib.contracts-spec/serialization";
|
|
175
|
+
var operationRegistry = null;
|
|
176
|
+
function createContractSpecOperationRegistry() {
|
|
177
|
+
const registry = new OperationSpecRegistry;
|
|
178
|
+
registry.register(DocsIndexQuery).register(ContractReferenceQuery).register(DocsGenerateCommand).register(DocsPublishCommand);
|
|
179
|
+
return registry;
|
|
180
|
+
}
|
|
181
|
+
function getContractSpecOperationRegistry() {
|
|
182
|
+
if (!operationRegistry) {
|
|
183
|
+
operationRegistry = createContractSpecOperationRegistry();
|
|
184
|
+
}
|
|
185
|
+
return operationRegistry;
|
|
186
|
+
}
|
|
187
|
+
function resolveOperationSpec(key, version) {
|
|
188
|
+
return getContractSpecOperationRegistry().get(key, version);
|
|
189
|
+
}
|
|
190
|
+
var eventRegistry = null;
|
|
191
|
+
function createContractSpecEventRegistry() {
|
|
192
|
+
const registry = new EventRegistry;
|
|
193
|
+
registry.register(DocsGeneratedEvent).register(DocsPublishedEvent);
|
|
194
|
+
return registry;
|
|
195
|
+
}
|
|
196
|
+
function getContractSpecEventRegistry() {
|
|
197
|
+
if (!eventRegistry) {
|
|
198
|
+
eventRegistry = createContractSpecEventRegistry();
|
|
199
|
+
}
|
|
200
|
+
return eventRegistry;
|
|
201
|
+
}
|
|
202
|
+
function resolveEventSpec(key, version) {
|
|
203
|
+
return getContractSpecEventRegistry().get(key, version);
|
|
204
|
+
}
|
|
205
|
+
var presentationRegistry = null;
|
|
206
|
+
function createContractSpecPresentationRegistry() {
|
|
207
|
+
const registry = new PresentationRegistry;
|
|
208
|
+
registry.register(DocsLayoutPresentation).register(DocsReferencePagePresentation);
|
|
209
|
+
return registry;
|
|
210
|
+
}
|
|
211
|
+
function getContractSpecPresentationRegistry() {
|
|
212
|
+
if (!presentationRegistry) {
|
|
213
|
+
presentationRegistry = createContractSpecPresentationRegistry();
|
|
214
|
+
}
|
|
215
|
+
return presentationRegistry;
|
|
216
|
+
}
|
|
217
|
+
function resolvePresentationSpec(key, version) {
|
|
218
|
+
return getContractSpecPresentationRegistry().get(key, version);
|
|
219
|
+
}
|
|
220
|
+
var dataViewRegistry = null;
|
|
221
|
+
function createContractSpecDataViewRegistry() {
|
|
222
|
+
const registry = new DataViewRegistry;
|
|
223
|
+
registry.register(DocsIndexDataView).register(ContractReferenceDataView).register(ExampleCatalogDataView);
|
|
224
|
+
return registry;
|
|
225
|
+
}
|
|
226
|
+
function getContractSpecDataViewRegistry() {
|
|
227
|
+
if (!dataViewRegistry) {
|
|
228
|
+
dataViewRegistry = createContractSpecDataViewRegistry();
|
|
229
|
+
}
|
|
230
|
+
return dataViewRegistry;
|
|
231
|
+
}
|
|
232
|
+
function resolveDataViewSpec(key, version) {
|
|
233
|
+
return getContractSpecDataViewRegistry().get(key, version);
|
|
234
|
+
}
|
|
235
|
+
var formRegistry = null;
|
|
236
|
+
function createContractSpecFormRegistry() {
|
|
237
|
+
const registry = new FormRegistry;
|
|
238
|
+
registry.register(DocsSearchForm);
|
|
239
|
+
return registry;
|
|
240
|
+
}
|
|
241
|
+
function getContractSpecFormRegistry() {
|
|
242
|
+
if (!formRegistry) {
|
|
243
|
+
formRegistry = createContractSpecFormRegistry();
|
|
244
|
+
}
|
|
245
|
+
return formRegistry;
|
|
246
|
+
}
|
|
247
|
+
function resolveFormSpec(key, _version) {
|
|
248
|
+
return getContractSpecFormRegistry().get(key);
|
|
249
|
+
}
|
|
250
|
+
function resolveSerializedOperationSpec(key, version) {
|
|
251
|
+
const spec = resolveOperationSpec(key, version);
|
|
252
|
+
return serializeOperationSpec(spec) ?? undefined;
|
|
253
|
+
}
|
|
254
|
+
function resolveSerializedEventSpec(key, version) {
|
|
255
|
+
const spec = resolveEventSpec(key, version);
|
|
256
|
+
return serializeEventSpec(spec) ?? undefined;
|
|
257
|
+
}
|
|
258
|
+
function resolveSerializedPresentationSpec(key, version) {
|
|
259
|
+
const spec = resolvePresentationSpec(key, version);
|
|
260
|
+
return serializePresentationSpec(spec) ?? undefined;
|
|
261
|
+
}
|
|
262
|
+
function resolveSerializedDataViewSpec(key, version) {
|
|
263
|
+
const spec = resolveDataViewSpec(key, version);
|
|
264
|
+
return serializeDataViewSpec(spec) ?? undefined;
|
|
265
|
+
}
|
|
266
|
+
function resolveSerializedFormSpec(key, version) {
|
|
267
|
+
const spec = resolveFormSpec(key, version);
|
|
268
|
+
return serializeFormSpec(spec) ?? undefined;
|
|
269
|
+
}
|
|
270
|
+
function resetContractSpecOperationRegistry() {
|
|
271
|
+
operationRegistry = null;
|
|
272
|
+
}
|
|
273
|
+
function resetContractSpecEventRegistry() {
|
|
274
|
+
eventRegistry = null;
|
|
275
|
+
}
|
|
276
|
+
function resetContractSpecPresentationRegistry() {
|
|
277
|
+
presentationRegistry = null;
|
|
278
|
+
}
|
|
279
|
+
function resetContractSpecDataViewRegistry() {
|
|
280
|
+
dataViewRegistry = null;
|
|
281
|
+
}
|
|
282
|
+
function resetContractSpecFormRegistry() {
|
|
283
|
+
formRegistry = null;
|
|
284
|
+
}
|
|
285
|
+
function resetAllContractSpecRegistries() {
|
|
286
|
+
resetContractSpecOperationRegistry();
|
|
287
|
+
resetContractSpecEventRegistry();
|
|
288
|
+
resetContractSpecPresentationRegistry();
|
|
289
|
+
resetContractSpecDataViewRegistry();
|
|
290
|
+
resetContractSpecFormRegistry();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/application/mcp/docsMcp.reference.ts
|
|
294
|
+
import { defaultDocRegistry as defaultDocRegistry2 } from "@contractspec/lib.contracts-spec/docs";
|
|
295
|
+
function normalizeText2(value) {
|
|
296
|
+
return value?.trim().toLowerCase() ?? "";
|
|
297
|
+
}
|
|
298
|
+
function routeFromDocIds(docIds) {
|
|
299
|
+
for (const docId of docIds ?? []) {
|
|
300
|
+
const doc = defaultDocRegistry2.get(docId);
|
|
301
|
+
if (doc)
|
|
302
|
+
return doc.route;
|
|
303
|
+
}
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
function toReference(spec, type, schema, policy) {
|
|
307
|
+
const title = spec.meta.title ?? spec.meta.key;
|
|
308
|
+
const route = routeFromDocIds(spec.meta.docId);
|
|
309
|
+
const description = spec.meta.description;
|
|
310
|
+
return {
|
|
311
|
+
key: spec.meta.key,
|
|
312
|
+
version: spec.meta.version,
|
|
313
|
+
type,
|
|
314
|
+
title,
|
|
315
|
+
description,
|
|
316
|
+
markdown: [
|
|
317
|
+
`# ${title}`,
|
|
318
|
+
`- Key: ${spec.meta.key}`,
|
|
319
|
+
`- Type: ${type}`,
|
|
320
|
+
`- Version: ${spec.meta.version}`,
|
|
321
|
+
route ? `- Docs route: ${route}` : "",
|
|
322
|
+
description ? `
|
|
323
|
+
${description}` : ""
|
|
324
|
+
].filter(Boolean).join(`
|
|
325
|
+
`),
|
|
326
|
+
...route ? { route } : {},
|
|
327
|
+
...schema ? { schema } : {},
|
|
328
|
+
...policy ? { policy } : {},
|
|
329
|
+
tags: spec.meta.tags ?? [],
|
|
330
|
+
owners: spec.meta.owners ?? [],
|
|
331
|
+
stability: spec.meta.stability
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function resolveContractReference(args) {
|
|
335
|
+
const includeSchema = args.includeSchema ?? false;
|
|
336
|
+
const requestedType = normalizeText2(args.type);
|
|
337
|
+
const operation = resolveOperationSpec(args.key, args.version);
|
|
338
|
+
if (operation && (!requestedType || requestedType === "operation" || requestedType === operation.meta.kind)) {
|
|
339
|
+
return {
|
|
340
|
+
reference: toReference(operation, operation.meta.kind, includeSchema ? resolveSerializedOperationSpec(args.key, args.version) : undefined, operation.policy)
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
const resolvers = [
|
|
344
|
+
{
|
|
345
|
+
type: "data-view",
|
|
346
|
+
spec: resolveDataViewSpec(args.key, args.version),
|
|
347
|
+
schema: includeSchema ? resolveSerializedDataViewSpec(args.key, args.version) : undefined
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
type: "form",
|
|
351
|
+
spec: resolveFormSpec(args.key, args.version),
|
|
352
|
+
schema: includeSchema ? resolveSerializedFormSpec(args.key, args.version) : undefined
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
type: "presentation",
|
|
356
|
+
spec: resolvePresentationSpec(args.key, args.version),
|
|
357
|
+
schema: includeSchema ? resolveSerializedPresentationSpec(args.key, args.version) : undefined
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
type: "event",
|
|
361
|
+
spec: resolveEventSpec(args.key, args.version),
|
|
362
|
+
schema: includeSchema ? resolveSerializedEventSpec(args.key, args.version) : undefined
|
|
363
|
+
}
|
|
364
|
+
];
|
|
365
|
+
for (const candidate of resolvers) {
|
|
366
|
+
if (candidate.spec && (!requestedType || requestedType === candidate.type)) {
|
|
367
|
+
return {
|
|
368
|
+
reference: toReference(candidate.spec, candidate.type, candidate.schema)
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
throw new Error(`Contract reference not found: ${args.key}`);
|
|
373
|
+
}
|
|
374
|
+
export {
|
|
375
|
+
toDocSummary,
|
|
376
|
+
searchDocs,
|
|
377
|
+
resolveContractReference,
|
|
378
|
+
listDocFacets,
|
|
379
|
+
getDocByRoute,
|
|
380
|
+
getDocById
|
|
381
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// src/application/mcp/docsMcp.data.ts
|
|
2
|
+
import { defaultDocRegistry } from "@contractspec/lib.contracts-spec/docs";
|
|
3
|
+
var DEFAULT_LIMIT = 20;
|
|
4
|
+
var MAX_LIMIT = 100;
|
|
5
|
+
function normalizeText(value) {
|
|
6
|
+
return value?.trim().toLowerCase() ?? "";
|
|
7
|
+
}
|
|
8
|
+
function normalizeRoute(route) {
|
|
9
|
+
const decoded = decodeURIComponent(route).trim();
|
|
10
|
+
if (!decoded)
|
|
11
|
+
return "/";
|
|
12
|
+
return decoded.startsWith("/") ? decoded : `/${decoded}`;
|
|
13
|
+
}
|
|
14
|
+
function normalizeTags(value) {
|
|
15
|
+
const tags = Array.isArray(value) ? value : value ? [value] : [];
|
|
16
|
+
return tags.map((tag) => normalizeText(tag)).filter(Boolean);
|
|
17
|
+
}
|
|
18
|
+
function clampLimit(limit) {
|
|
19
|
+
if (!limit || Number.isNaN(limit))
|
|
20
|
+
return DEFAULT_LIMIT;
|
|
21
|
+
return Math.min(Math.max(limit, 1), MAX_LIMIT);
|
|
22
|
+
}
|
|
23
|
+
function clampOffset(offset) {
|
|
24
|
+
if (!offset || Number.isNaN(offset))
|
|
25
|
+
return 0;
|
|
26
|
+
return Math.max(offset, 0);
|
|
27
|
+
}
|
|
28
|
+
function toDocSummary({ block, route }) {
|
|
29
|
+
return {
|
|
30
|
+
id: block.id,
|
|
31
|
+
title: block.title,
|
|
32
|
+
summary: block.summary ?? "",
|
|
33
|
+
route,
|
|
34
|
+
visibility: block.visibility ?? "public",
|
|
35
|
+
kind: block.kind ?? "reference",
|
|
36
|
+
version: block.version ?? "1.0.0",
|
|
37
|
+
tags: block.tags ?? []
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function scoreDoc(route, query) {
|
|
41
|
+
if (!query)
|
|
42
|
+
return 1;
|
|
43
|
+
const tokens = query.split(/\s+/).filter(Boolean);
|
|
44
|
+
const title = normalizeText(route.block.title);
|
|
45
|
+
const id = normalizeText(route.block.id);
|
|
46
|
+
const summary = normalizeText(route.block.summary);
|
|
47
|
+
const body = normalizeText(route.block.body);
|
|
48
|
+
const path = normalizeText(route.route);
|
|
49
|
+
const tags = (route.block.tags ?? []).map((tag) => normalizeText(tag));
|
|
50
|
+
const haystack = [title, id, summary, body, path, ...tags].join(" ");
|
|
51
|
+
if (tokens.some((token) => !haystack.includes(token)))
|
|
52
|
+
return 0;
|
|
53
|
+
let score = 0;
|
|
54
|
+
for (const token of tokens) {
|
|
55
|
+
if (id.includes(token))
|
|
56
|
+
score += 8;
|
|
57
|
+
if (title.includes(token))
|
|
58
|
+
score += 7;
|
|
59
|
+
if (tags.some((tag) => tag.includes(token)))
|
|
60
|
+
score += 5;
|
|
61
|
+
if (summary.includes(token))
|
|
62
|
+
score += 4;
|
|
63
|
+
if (path.includes(token))
|
|
64
|
+
score += 3;
|
|
65
|
+
if (body.includes(token))
|
|
66
|
+
score += 2;
|
|
67
|
+
}
|
|
68
|
+
return score;
|
|
69
|
+
}
|
|
70
|
+
function searchDocs(routes, args) {
|
|
71
|
+
const query = normalizeText(typeof args.query === "string" ? args.query : undefined);
|
|
72
|
+
const tags = normalizeTags(args.tag);
|
|
73
|
+
const visibility = normalizeText(typeof args.visibility === "string" ? args.visibility : undefined);
|
|
74
|
+
const kind = normalizeText(typeof args.kind === "string" ? args.kind : undefined);
|
|
75
|
+
const limit = clampLimit(typeof args.limit === "number" ? args.limit : undefined);
|
|
76
|
+
const offset = clampOffset(typeof args.offset === "number" ? args.offset : undefined);
|
|
77
|
+
const ranked = routes.map((route) => ({
|
|
78
|
+
doc: toDocSummary(route),
|
|
79
|
+
score: scoreDoc(route, query)
|
|
80
|
+
})).filter(({ doc, score }) => {
|
|
81
|
+
const matchesQuery = query ? score > 0 : true;
|
|
82
|
+
const matchesTags = tags.length ? tags.every((tag) => doc.tags.some((docTag) => normalizeText(docTag).includes(tag))) : true;
|
|
83
|
+
const matchesVisibility = visibility ? normalizeText(doc.visibility) === visibility : true;
|
|
84
|
+
const matchesKind = kind ? normalizeText(doc.kind) === kind : true;
|
|
85
|
+
return matchesQuery && matchesTags && matchesVisibility && matchesKind;
|
|
86
|
+
}).sort((left, right) => {
|
|
87
|
+
if (right.score !== left.score)
|
|
88
|
+
return right.score - left.score;
|
|
89
|
+
return left.doc.title.localeCompare(right.doc.title);
|
|
90
|
+
});
|
|
91
|
+
const docs = ranked.slice(offset, offset + limit).map(({ doc }) => doc);
|
|
92
|
+
const nextOffset = offset + docs.length < ranked.length ? offset + docs.length : undefined;
|
|
93
|
+
return {
|
|
94
|
+
docs,
|
|
95
|
+
items: docs,
|
|
96
|
+
total: ranked.length,
|
|
97
|
+
...nextOffset != null ? { nextOffset } : {}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function getDocById(id) {
|
|
101
|
+
const normalizedId = decodeURIComponent(id);
|
|
102
|
+
const found = defaultDocRegistry.get(normalizedId);
|
|
103
|
+
if (!found)
|
|
104
|
+
return;
|
|
105
|
+
return {
|
|
106
|
+
doc: toDocSummary(found),
|
|
107
|
+
content: String(found.block.body ?? "")
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function getDocByRoute(routes, routePath) {
|
|
111
|
+
const normalizedPath = normalizeRoute(routePath);
|
|
112
|
+
const found = routes.find((route) => normalizeRoute(route.route) === normalizedPath);
|
|
113
|
+
if (!found)
|
|
114
|
+
return;
|
|
115
|
+
return {
|
|
116
|
+
doc: toDocSummary(found),
|
|
117
|
+
content: String(found.block.body ?? "")
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function listDocFacets(routes) {
|
|
121
|
+
const tags = new Map;
|
|
122
|
+
const kinds = new Map;
|
|
123
|
+
const visibilities = new Map;
|
|
124
|
+
for (const route of routes) {
|
|
125
|
+
const kind = route.block.kind ?? "reference";
|
|
126
|
+
const visibility = route.block.visibility ?? "public";
|
|
127
|
+
kinds.set(kind, (kinds.get(kind) ?? 0) + 1);
|
|
128
|
+
visibilities.set(visibility, (visibilities.get(visibility) ?? 0) + 1);
|
|
129
|
+
for (const tag of route.block.tags ?? []) {
|
|
130
|
+
tags.set(tag, (tags.get(tag) ?? 0) + 1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const toEntries = (values, key) => [...values.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])).map(([value, count]) => ({ [key]: value, count }));
|
|
134
|
+
return {
|
|
135
|
+
totalDocs: routes.length,
|
|
136
|
+
tags: toEntries(tags, "tag"),
|
|
137
|
+
kinds: toEntries(kinds, "kind"),
|
|
138
|
+
visibilities: toEntries(visibilities, "visibility")
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
export {
|
|
142
|
+
toDocSummary,
|
|
143
|
+
searchDocs,
|
|
144
|
+
listDocFacets,
|
|
145
|
+
getDocByRoute,
|
|
146
|
+
getDocById
|
|
147
|
+
};
|