@cyanmycelium/mcp-persistence 0.0.0-stage → 0.2.0
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/LICENSE +201 -0
- package/README.md +189 -2
- package/contracts/persistence.v1.json +9606 -0
- package/dist/conformance/persistence.conformance.d.ts +4 -0
- package/dist/conformance/persistence.conformance.js +56 -0
- package/dist/conformance/persistence.conformance.js.map +1 -0
- package/dist/contract/errors.d.ts +5 -0
- package/dist/contract/errors.js +9 -0
- package/dist/contract/errors.js.map +1 -0
- package/dist/contract/persistence.protocol.d.ts +83 -0
- package/dist/contract/persistence.protocol.js +75 -0
- package/dist/contract/persistence.protocol.js.map +1 -0
- package/dist/contract/persistence.store.d.ts +29 -0
- package/dist/contract/persistence.store.js +2 -0
- package/dist/contract/persistence.store.js.map +1 -0
- package/dist/contract/persistence.types.d.ts +115 -0
- package/dist/contract/persistence.types.js +2 -0
- package/dist/contract/persistence.types.js.map +1 -0
- package/dist/contract/validation.d.ts +7 -0
- package/dist/contract/validation.js +43 -0
- package/dist/contract/validation.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/provider/artifact.http.d.ts +8 -0
- package/dist/provider/artifact.http.js +69 -0
- package/dist/provider/artifact.http.js.map +1 -0
- package/dist/provider/main.d.ts +2 -0
- package/dist/provider/main.js +82 -0
- package/dist/provider/main.js.map +1 -0
- package/dist/server/declaration.d.ts +18 -0
- package/dist/server/declaration.js +23 -0
- package/dist/server/declaration.js.map +1 -0
- package/dist/server/persistence.behavior.d.ts +16 -0
- package/dist/server/persistence.behavior.js +173 -0
- package/dist/server/persistence.behavior.js.map +1 -0
- package/dist/server/persistence.slot.store.d.ts +45 -0
- package/dist/server/persistence.slot.store.js +94 -0
- package/dist/server/persistence.slot.store.js.map +1 -0
- package/dist/sqlite/sqlite.persistence.store.d.ts +72 -0
- package/dist/sqlite/sqlite.persistence.store.js +561 -0
- package/dist/sqlite/sqlite.persistence.store.js.map +1 -0
- package/docs/assets/logo.png +0 -0
- package/docs/assets/mcp-broker-family.png +0 -0
- package/docs/mcp-contract.md +189 -0
- package/package.json +79 -4
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IPersistenceStore } from "../contract/persistence.store.js";
|
|
2
|
+
import type { IStoreContext } from "../contract/persistence.types.js";
|
|
3
|
+
/** Reuse on a direct backend or a PersistenceSlotStore with the same caller privileges. */
|
|
4
|
+
export declare function describePersistenceStoreConformance(name: string, getStore: () => IPersistenceStore, context?: IStoreContext): void;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
/** Reuse on a direct backend or a PersistenceSlotStore with the same caller privileges. */
|
|
4
|
+
export function describePersistenceStoreConformance(name, getStore, context = { subject: "conformance" }) {
|
|
5
|
+
describe(name, () => {
|
|
6
|
+
const namespace = "explorer";
|
|
7
|
+
const put = (id, key, expectedRevision = 0, payload = { saved: true }) => ({ namespace, id, kind: "checkpoint", schemaVersion: "1", payload, expectedRevision, references: [], idempotencyKey: key });
|
|
8
|
+
it("keeps immutable revisions and rejects a stale write", async () => {
|
|
9
|
+
const store = getStore(), id = randomUUID();
|
|
10
|
+
const first = await store.putAsync(put(id, randomUUID()), context);
|
|
11
|
+
await store.putAsync(put(id, randomUUID(), 1, { saved: false }), context);
|
|
12
|
+
expect((await store.getAsync({ namespace, id, revision: 1 }, context)).payload).toEqual(first.payload);
|
|
13
|
+
await expect(store.putAsync(put(id, randomUUID(), 1), context)).rejects.toMatchObject({ code: "revision_conflict" });
|
|
14
|
+
});
|
|
15
|
+
it("replays a mutation and rejects reuse with different content", async () => {
|
|
16
|
+
const store = getStore(), id = randomUUID(), request = put(id, randomUUID());
|
|
17
|
+
const result = await store.putAsync(request, context);
|
|
18
|
+
expect(await store.putAsync(request, context)).toEqual(result);
|
|
19
|
+
await expect(store.putAsync({ ...request, payload: { saved: false } }, context)).rejects.toMatchObject({ code: "idempotency_conflict" });
|
|
20
|
+
expect((await store.listRevisionsAsync({ namespace, id }, context)).items).toHaveLength(1);
|
|
21
|
+
});
|
|
22
|
+
it("serializes competing writes to the same revision", async () => {
|
|
23
|
+
const store = getStore(), id = randomUUID();
|
|
24
|
+
await store.putAsync(put(id, randomUUID()), context);
|
|
25
|
+
const results = await Promise.allSettled([store.putAsync(put(id, randomUUID(), 1), context), store.putAsync(put(id, randomUUID(), 1), context)]);
|
|
26
|
+
expect(results.filter(r => r.status === "fulfilled")).toHaveLength(1);
|
|
27
|
+
expect(results.filter(r => r.status === "rejected")).toHaveLength(1);
|
|
28
|
+
});
|
|
29
|
+
it("deletes logically while keeping pinned history", async () => {
|
|
30
|
+
const store = getStore(), id = randomUUID();
|
|
31
|
+
await store.putAsync(put(id, randomUUID()), context);
|
|
32
|
+
const deleted = await store.deleteAsync({ namespace, id, expectedRevision: 1, idempotencyKey: randomUUID() }, context);
|
|
33
|
+
expect(deleted.deleted).toBe(true);
|
|
34
|
+
await expect(store.getAsync({ namespace, id }, context)).rejects.toMatchObject({ code: "not_found" });
|
|
35
|
+
expect((await store.getAsync({ namespace, id, revision: 1 }, context)).revision).toBe(1);
|
|
36
|
+
await expect(store.putAsync(put(id, randomUUID()), context)).rejects.toMatchObject({ code: "revision_conflict" });
|
|
37
|
+
});
|
|
38
|
+
it("rejects a missing artifact reference transactionally", async () => {
|
|
39
|
+
const store = getStore(), id = randomUUID();
|
|
40
|
+
await expect(store.putAsync({ ...put(id, randomUUID()), references: [{ type: "artifact", namespace, id: randomUUID() }] }, context)).rejects.toMatchObject({ code: "not_found" });
|
|
41
|
+
await expect(store.getAsync({ namespace, id }, context)).rejects.toMatchObject({ code: "not_found" });
|
|
42
|
+
});
|
|
43
|
+
it("binds signed pagination to its filters", async () => {
|
|
44
|
+
const store = getStore();
|
|
45
|
+
for (let i = 0; i < 3; ++i)
|
|
46
|
+
await store.putAsync(put(randomUUID(), randomUUID()), context);
|
|
47
|
+
const page = await store.listAsync({ namespace, limit: 1 }, context);
|
|
48
|
+
expect(page.items).toHaveLength(1);
|
|
49
|
+
expect(page.nextCursor).toBeTruthy();
|
|
50
|
+
const next = await store.listAsync({ namespace, limit: 1, cursor: page.nextCursor }, context);
|
|
51
|
+
expect(next.items[0].id).not.toBe(page.items[0].id);
|
|
52
|
+
await expect(store.listAsync({ namespace, kind: "other", cursor: page.nextCursor }, context)).rejects.toMatchObject({ code: "invalid_request" });
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=persistence.conformance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.conformance.js","sourceRoot":"","sources":["../../src/conformance/persistence.conformance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type PersistenceErrorCode = "not_found" | "revision_conflict" | "idempotency_conflict" | "policy_denied" | "quota_exceeded" | "transfer_incomplete" | "integrity_error" | "invalid_request" | "unsupported_format" | "retention_blocked" | "busy" | "store_unavailable";
|
|
2
|
+
export declare class PersistenceError extends Error {
|
|
3
|
+
readonly code: PersistenceErrorCode;
|
|
4
|
+
constructor(code: PersistenceErrorCode, message: string);
|
|
5
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/contract/errors.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAClB;IAArB,YAAqB,IAA0B,EAAE,OAAe;QAC5D,KAAK,CAAC,OAAO,CAAC,CAAC;QADE,SAAI,GAAJ,IAAI,CAAsB;QAE3C,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACnC,CAAC;CACJ"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/** Backend-independent wire contract. Generate contracts/persistence.v1.json from these definitions. */
|
|
2
|
+
type Schema = Record<string, unknown>;
|
|
3
|
+
export declare const PERSISTENCE_DEFINITIONS: Record<string, Schema>;
|
|
4
|
+
export declare const PERSISTENCE_TOOL_DESCRIPTIONS: Record<string, string>;
|
|
5
|
+
/** Plain MCP tools/list descriptors, including inputSchema and outputSchema. */
|
|
6
|
+
export declare const PERSISTENCE_TOOLS: {
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
inputSchema: {
|
|
10
|
+
$defs: Record<string, Schema>;
|
|
11
|
+
type: string;
|
|
12
|
+
properties: Record<string, Schema>;
|
|
13
|
+
required: string[];
|
|
14
|
+
additionalProperties: boolean;
|
|
15
|
+
};
|
|
16
|
+
outputSchema: {
|
|
17
|
+
type: string;
|
|
18
|
+
anyOf: Schema[];
|
|
19
|
+
$defs: Record<string, Schema>;
|
|
20
|
+
};
|
|
21
|
+
}[];
|
|
22
|
+
export declare const PERSISTENCE_RESOURCE_TEMPLATES: {
|
|
23
|
+
uriTemplate: string;
|
|
24
|
+
name: string;
|
|
25
|
+
mimeType: string;
|
|
26
|
+
}[];
|
|
27
|
+
export declare const PERSISTENCE_PROTOCOL: {
|
|
28
|
+
contract: string;
|
|
29
|
+
revision: number;
|
|
30
|
+
tools: {
|
|
31
|
+
name: string;
|
|
32
|
+
description: string;
|
|
33
|
+
inputSchema: {
|
|
34
|
+
$defs: Record<string, Schema>;
|
|
35
|
+
type: string;
|
|
36
|
+
properties: Record<string, Schema>;
|
|
37
|
+
required: string[];
|
|
38
|
+
additionalProperties: boolean;
|
|
39
|
+
};
|
|
40
|
+
outputSchema: {
|
|
41
|
+
type: string;
|
|
42
|
+
anyOf: Schema[];
|
|
43
|
+
$defs: Record<string, Schema>;
|
|
44
|
+
};
|
|
45
|
+
}[];
|
|
46
|
+
resources: {
|
|
47
|
+
uri: string;
|
|
48
|
+
name: string;
|
|
49
|
+
mimeType: string;
|
|
50
|
+
description: string;
|
|
51
|
+
}[];
|
|
52
|
+
resourceTemplates: {
|
|
53
|
+
uriTemplate: string;
|
|
54
|
+
name: string;
|
|
55
|
+
mimeType: string;
|
|
56
|
+
}[];
|
|
57
|
+
transfer: {
|
|
58
|
+
authentication: string;
|
|
59
|
+
routes: ({
|
|
60
|
+
method: string;
|
|
61
|
+
path: string;
|
|
62
|
+
request: string;
|
|
63
|
+
success: number;
|
|
64
|
+
precondition: string;
|
|
65
|
+
response?: undefined;
|
|
66
|
+
} | {
|
|
67
|
+
method: string;
|
|
68
|
+
path: string;
|
|
69
|
+
response: string;
|
|
70
|
+
success: number;
|
|
71
|
+
precondition: string;
|
|
72
|
+
request?: undefined;
|
|
73
|
+
})[];
|
|
74
|
+
errors: {
|
|
75
|
+
400: string[];
|
|
76
|
+
403: string[];
|
|
77
|
+
404: string[];
|
|
78
|
+
413: string[];
|
|
79
|
+
500: string[];
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const string = { type: "string", minLength: 1, maxLength: 256 };
|
|
2
|
+
const segment = { type: "string", pattern: "^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$", maxLength: 128 };
|
|
3
|
+
const revision = { type: "integer", minimum: 1, maximum: Number.MAX_SAFE_INTEGER };
|
|
4
|
+
const expectedRevision = { ...revision, minimum: 0 };
|
|
5
|
+
const date = { type: "string", format: "date-time" };
|
|
6
|
+
const key = { ...string, maxLength: 128 };
|
|
7
|
+
const mimeType = { type: "string", maxLength: 128, pattern: "^[a-zA-Z0-9!#$&^_.+-]+/[a-zA-Z0-9!#$&^_.+-]+$" };
|
|
8
|
+
const ref = (name) => ({ $ref: `#/$defs/${name}` });
|
|
9
|
+
function object(properties, required = Object.keys(properties)) { return { type: "object", properties, required, additionalProperties: false }; }
|
|
10
|
+
const location = { namespace: segment, id: segment };
|
|
11
|
+
const page = { limit: { type: "integer", minimum: 1, maximum: 200 }, cursor: { type: "string", minLength: 1, maxLength: 4096 } };
|
|
12
|
+
const finish = { namespace: segment, uploadId: segment, idempotencyKey: key };
|
|
13
|
+
const errorCodes = ["not_found", "revision_conflict", "idempotency_conflict", "policy_denied", "quota_exceeded", "transfer_incomplete", "integrity_error", "invalid_request", "unsupported_format", "retention_blocked", "busy", "store_unavailable"];
|
|
14
|
+
export const PERSISTENCE_DEFINITIONS = {
|
|
15
|
+
Json: { anyOf: [{ type: "null" }, { type: "boolean" }, { type: "number" }, { type: "string" }, { type: "array", items: ref("Json") }, { type: "object", additionalProperties: ref("Json") }] },
|
|
16
|
+
Reference: { oneOf: [object({ type: { const: "document" }, ...location, revision }), object({ type: { const: "artifact" }, ...location })] },
|
|
17
|
+
Hash: object({ algorithm: { const: "sha256" }, value: { type: "string", pattern: "^[a-f0-9]{64}$" } }),
|
|
18
|
+
Document: object({ ...location, kind: string, schemaVersion: string, revision, createdAt: date, updatedAt: date, deleted: { type: "boolean" }, payload: ref("Json"), references: { type: "array", maxItems: 256, items: ref("Reference") } }),
|
|
19
|
+
Artifact: object({ ...location, mimeType, size: { type: "integer", minimum: 0, maximum: Number.MAX_SAFE_INTEGER }, hash: ref("Hash"), createdAt: date, provenance: ref("Json") }),
|
|
20
|
+
Upload: object({ namespace: segment, artifactId: segment, uploadId: segment, expiresAt: date }),
|
|
21
|
+
DocumentPage: object({ items: { type: "array", maxItems: 200, items: ref("Document") }, nextCursor: page.cursor }, ["items"]),
|
|
22
|
+
Capabilities: object({ contract: { const: "persistence.v1" }, durability: { enum: ["file-sync", "file-and-directory-sync"] }, limits: object(Object.fromEntries(["maxDocumentBytes", "maxArtifactBytes", "maxReferences", "maxPageSize", "maxNamespaceBytes", "idempotencyRetentionSeconds"].map(k => [k, { type: "integer", minimum: 1 }]))) }),
|
|
23
|
+
Acknowledgement: object({ ok: { const: true } }),
|
|
24
|
+
Error: object({ error: object({ code: { enum: errorCodes }, message: { type: "string" } }) }),
|
|
25
|
+
};
|
|
26
|
+
export const PERSISTENCE_TOOL_DESCRIPTIONS = {
|
|
27
|
+
capabilities: "Read protocol limits and durability guarantees.",
|
|
28
|
+
get: "Read a document head or an immutable revision.", put: "Create or update with expectedRevision and an idempotency key.",
|
|
29
|
+
list: "List documents you may read, with bounded pagination.", listRevisions: "List immutable revisions of a document you may read.",
|
|
30
|
+
delete: "Add a tombstone; keep history and referenced files.", purge: "Permanently purge an unreferenced revision or artifact after retention.",
|
|
31
|
+
beginArtifact: "Reserve an upload. Transfer bytes over the authenticated HTTP endpoint.",
|
|
32
|
+
commitArtifact: "Verify and publish a completed upload atomically.", abortArtifact: "Abort an inactive upload.",
|
|
33
|
+
getArtifact: "Read artifact metadata; bytes use the authenticated HTTP endpoint.",
|
|
34
|
+
uploadAccess: "Authorize an upload stream and read its target metadata. Used by the transfer endpoint.",
|
|
35
|
+
};
|
|
36
|
+
const inputs = {
|
|
37
|
+
capabilities: { input: object({}), result: "Capabilities" },
|
|
38
|
+
get: { input: object({ ...location, revision }, ["namespace", "id"]), result: "Document" },
|
|
39
|
+
put: { input: object({ ...location, expectedRevision, kind: string, schemaVersion: string, payload: ref("Json"), references: { type: "array", maxItems: 256, items: ref("Reference") }, idempotencyKey: key }), result: "Document" },
|
|
40
|
+
list: { input: object({ namespace: segment, kind: string, includeDeleted: { type: "boolean" }, ...page }, ["namespace"]), result: "DocumentPage" },
|
|
41
|
+
listRevisions: { input: object({ ...location, ...page }, ["namespace", "id"]), result: "DocumentPage" },
|
|
42
|
+
delete: { input: object({ ...location, expectedRevision: revision, idempotencyKey: key }), result: "Document" },
|
|
43
|
+
purge: { input: object({ namespace: segment, target: { oneOf: [object({ type: { const: "document" }, id: segment, revision }), object({ type: { const: "artifact" }, id: segment })] }, idempotencyKey: key }), result: "Acknowledgement" },
|
|
44
|
+
beginArtifact: { input: object({ namespace: segment, mimeType, size: { type: "integer", minimum: 0, maximum: Number.MAX_SAFE_INTEGER }, hash: ref("Hash"), provenance: ref("Json"), idempotencyKey: key }), result: "Upload" },
|
|
45
|
+
commitArtifact: { input: object(finish), result: "Artifact" },
|
|
46
|
+
abortArtifact: { input: object(finish), result: "Acknowledgement" },
|
|
47
|
+
uploadAccess: { input: object({ namespace: segment, uploadId: segment }), result: "Upload" },
|
|
48
|
+
getArtifact: { input: object(location), result: "Artifact" },
|
|
49
|
+
};
|
|
50
|
+
/** Plain MCP tools/list descriptors, including inputSchema and outputSchema. */
|
|
51
|
+
export const PERSISTENCE_TOOLS = Object.entries(inputs).map(([name, definition]) => ({
|
|
52
|
+
name: `persistence.${name}`, description: PERSISTENCE_TOOL_DESCRIPTIONS[name],
|
|
53
|
+
inputSchema: { ...definition.input, $defs: PERSISTENCE_DEFINITIONS },
|
|
54
|
+
outputSchema: { type: "object", anyOf: [ref(definition.result), ref("Error")], $defs: PERSISTENCE_DEFINITIONS },
|
|
55
|
+
}));
|
|
56
|
+
export const PERSISTENCE_RESOURCE_TEMPLATES = [
|
|
57
|
+
{ uriTemplate: "persistence://documents/{namespace}/{id}", name: "Document head", mimeType: "application/json" },
|
|
58
|
+
{ uriTemplate: "persistence://documents/{namespace}/{id}/revisions/{revision}", name: "Pinned document revision", mimeType: "application/json" },
|
|
59
|
+
{ uriTemplate: "persistence://artifacts/{namespace}/{id}", name: "Artifact metadata", mimeType: "application/json" },
|
|
60
|
+
];
|
|
61
|
+
export const PERSISTENCE_PROTOCOL = {
|
|
62
|
+
contract: "persistence.v1", revision: 1,
|
|
63
|
+
tools: PERSISTENCE_TOOLS,
|
|
64
|
+
resources: [{ uri: "persistence://capabilities", name: "Persistence capabilities", mimeType: "application/json", description: PERSISTENCE_TOOL_DESCRIPTIONS.capabilities }],
|
|
65
|
+
resourceTemplates: PERSISTENCE_RESOURCE_TEMPLATES,
|
|
66
|
+
transfer: {
|
|
67
|
+
authentication: "The same bearer as the MCP client; every request is reauthorized through the broker. No reusable bearer URLs.",
|
|
68
|
+
routes: [
|
|
69
|
+
{ method: "PUT", path: "/uploads/{namespace}/{uploadId}", request: "application/octet-stream raw bytes", success: 204, precondition: "beginArtifact; uploadAccess authorization" },
|
|
70
|
+
{ method: "GET", path: "/artifacts/{namespace}/{id}", response: "raw bytes with stored MIME type and Content-Length", success: 200, precondition: "committed artifact; getArtifact authorization" },
|
|
71
|
+
],
|
|
72
|
+
errors: { 400: ["invalid_request", "transfer_incomplete", "integrity_error"], 403: ["policy_denied"], 404: ["not_found"], 413: ["quota_exceeded"], 500: ["store_unavailable", "busy"] },
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=persistence.protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.protocol.js","sourceRoot":"","sources":["../../src/contract/persistence.protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { IArtifact, IBeginArtifact, IDeleteDocument, IDocument, IDocumentKey, IFinishArtifact, IListDocuments, IPage, IPageRequest, IPersistenceCapabilities, IPurge, IPutDocument, IStoreContext, IUpload } from "./persistence.types.js";
|
|
2
|
+
/** Trusted backend contract, following the direct-store/remote-slot pattern of mcp-vault. */
|
|
3
|
+
export interface IPersistenceStore {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
getCapabilitiesAsync(signal?: AbortSignal): Promise<IPersistenceCapabilities>;
|
|
6
|
+
getAsync(request: IDocumentKey & {
|
|
7
|
+
revision?: number;
|
|
8
|
+
}, context: IStoreContext): Promise<IDocument>;
|
|
9
|
+
putAsync(request: IPutDocument, context: IStoreContext): Promise<IDocument>;
|
|
10
|
+
listAsync(request: IListDocuments, context: IStoreContext): Promise<IPage<IDocument>>;
|
|
11
|
+
listRevisionsAsync(request: IDocumentKey & IPageRequest, context: IStoreContext): Promise<IPage<IDocument>>;
|
|
12
|
+
/** Adds a tombstone; does not purge history or files. */
|
|
13
|
+
deleteAsync(request: IDeleteDocument, context: IStoreContext): Promise<IDocument>;
|
|
14
|
+
beginArtifactAsync(request: IBeginArtifact, context: IStoreContext): Promise<IUpload>;
|
|
15
|
+
inspectUploadAsync(namespace: string, uploadId: string, context: IStoreContext): Promise<IUpload>;
|
|
16
|
+
/** Stream bytes internally. The public endpoint must authenticate and authorize separately. */
|
|
17
|
+
writeArtifactAsync(request: {
|
|
18
|
+
namespace: string;
|
|
19
|
+
uploadId: string;
|
|
20
|
+
bytes: AsyncIterable<Uint8Array>;
|
|
21
|
+
}, context: IStoreContext): Promise<void>;
|
|
22
|
+
commitArtifactAsync(request: IFinishArtifact, context: IStoreContext): Promise<IArtifact>;
|
|
23
|
+
abortArtifactAsync(request: IFinishArtifact, context: IStoreContext): Promise<void>;
|
|
24
|
+
getArtifactAsync(request: IDocumentKey, context: IStoreContext): Promise<IArtifact>;
|
|
25
|
+
readArtifactAsync(request: IDocumentKey, context: IStoreContext): Promise<AsyncIterable<Uint8Array>>;
|
|
26
|
+
/** Physical purge, subject to retention and incoming-reference checks. */
|
|
27
|
+
purgeAsync(request: IPurge, context: IStoreContext): Promise<void>;
|
|
28
|
+
closeAsync(): Promise<void>;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.store.js","sourceRoot":"","sources":["../../src/contract/persistence.store.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** persistence.v1 documents and immutable artifacts. */
|
|
2
|
+
export type Json = null | boolean | number | string | Json[] | {
|
|
3
|
+
[key: string]: Json;
|
|
4
|
+
};
|
|
5
|
+
export interface IDocumentKey {
|
|
6
|
+
namespace: string;
|
|
7
|
+
id: string;
|
|
8
|
+
}
|
|
9
|
+
export interface IDocumentReference extends IDocumentKey {
|
|
10
|
+
type: "document";
|
|
11
|
+
revision: number;
|
|
12
|
+
}
|
|
13
|
+
export interface IArtifactReference {
|
|
14
|
+
type: "artifact";
|
|
15
|
+
namespace: string;
|
|
16
|
+
id: string;
|
|
17
|
+
}
|
|
18
|
+
export interface IDocument extends IDocumentKey {
|
|
19
|
+
kind: string;
|
|
20
|
+
schemaVersion: string;
|
|
21
|
+
revision: number;
|
|
22
|
+
createdAt: string;
|
|
23
|
+
updatedAt: string;
|
|
24
|
+
deleted: boolean;
|
|
25
|
+
payload: Json;
|
|
26
|
+
references: readonly (IDocumentReference | IArtifactReference)[];
|
|
27
|
+
}
|
|
28
|
+
export interface IMutation {
|
|
29
|
+
idempotencyKey: string;
|
|
30
|
+
}
|
|
31
|
+
export interface IPutDocument extends IDocumentKey, IMutation {
|
|
32
|
+
/** Zero creates only; otherwise must equal the current revision, including a tombstone. */
|
|
33
|
+
expectedRevision: number;
|
|
34
|
+
kind: string;
|
|
35
|
+
schemaVersion: string;
|
|
36
|
+
payload: Json;
|
|
37
|
+
references: readonly (IDocumentReference | IArtifactReference)[];
|
|
38
|
+
}
|
|
39
|
+
export interface IPageRequest {
|
|
40
|
+
limit?: number;
|
|
41
|
+
cursor?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface IPage<T> {
|
|
44
|
+
items: readonly T[];
|
|
45
|
+
nextCursor?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface IListDocuments extends IPageRequest {
|
|
48
|
+
namespace: string;
|
|
49
|
+
kind?: string;
|
|
50
|
+
includeDeleted?: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface IDeleteDocument extends IDocumentKey, IMutation {
|
|
53
|
+
expectedRevision: number;
|
|
54
|
+
}
|
|
55
|
+
export interface IArtifact {
|
|
56
|
+
namespace: string;
|
|
57
|
+
id: string;
|
|
58
|
+
mimeType: string;
|
|
59
|
+
size: number;
|
|
60
|
+
hash: {
|
|
61
|
+
algorithm: "sha256";
|
|
62
|
+
value: string;
|
|
63
|
+
};
|
|
64
|
+
createdAt: string;
|
|
65
|
+
provenance: Json;
|
|
66
|
+
}
|
|
67
|
+
export interface IBeginArtifact extends IMutation {
|
|
68
|
+
namespace: string;
|
|
69
|
+
mimeType: string;
|
|
70
|
+
size: number;
|
|
71
|
+
hash: {
|
|
72
|
+
algorithm: "sha256";
|
|
73
|
+
value: string;
|
|
74
|
+
};
|
|
75
|
+
provenance: Json;
|
|
76
|
+
}
|
|
77
|
+
/** No physical path or reusable bearer ticket in persisted/replayed store results. */
|
|
78
|
+
export interface IUpload {
|
|
79
|
+
namespace: string;
|
|
80
|
+
artifactId: string;
|
|
81
|
+
uploadId: string;
|
|
82
|
+
expiresAt: string;
|
|
83
|
+
}
|
|
84
|
+
export interface IFinishArtifact extends IMutation {
|
|
85
|
+
namespace: string;
|
|
86
|
+
uploadId: string;
|
|
87
|
+
}
|
|
88
|
+
export interface IPurge extends IMutation {
|
|
89
|
+
namespace: string;
|
|
90
|
+
target: {
|
|
91
|
+
type: "document";
|
|
92
|
+
id: string;
|
|
93
|
+
revision: number;
|
|
94
|
+
} | {
|
|
95
|
+
type: "artifact";
|
|
96
|
+
id: string;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** Internal trusted context, supplied by middleware; never accepted from tool arguments. */
|
|
100
|
+
export interface IStoreContext {
|
|
101
|
+
readonly subject: string;
|
|
102
|
+
readonly signal?: AbortSignal;
|
|
103
|
+
}
|
|
104
|
+
export interface IPersistenceCapabilities {
|
|
105
|
+
contract: "persistence.v1";
|
|
106
|
+
durability: "file-sync" | "file-and-directory-sync";
|
|
107
|
+
limits: {
|
|
108
|
+
maxDocumentBytes: number;
|
|
109
|
+
maxArtifactBytes: number;
|
|
110
|
+
maxReferences: number;
|
|
111
|
+
maxPageSize: number;
|
|
112
|
+
maxNamespaceBytes: number;
|
|
113
|
+
idempotencyRetentionSeconds: number;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.types.js","sourceRoot":"","sources":["../../src/contract/persistence.types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { PersistenceError } from "./errors.js";
|
|
2
|
+
export declare function fail(code: ConstructorParameters<typeof PersistenceError>[0], message: string): never;
|
|
3
|
+
export declare function segment(value: unknown): string;
|
|
4
|
+
export declare function integer(value: unknown, min?: number, max?: number): number;
|
|
5
|
+
export declare function text(value: unknown, max?: number): string;
|
|
6
|
+
export declare function canonical(value: unknown): string;
|
|
7
|
+
export declare function digest(value: unknown): string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { PersistenceError } from "./errors.js";
|
|
3
|
+
export function fail(code, message) {
|
|
4
|
+
throw new PersistenceError(code, message);
|
|
5
|
+
}
|
|
6
|
+
export function segment(value) {
|
|
7
|
+
if (typeof value !== "string" || !/^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$/.test(value))
|
|
8
|
+
fail("invalid_request", "Invalid resource segment");
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
export function integer(value, min = 0, max = Number.MAX_SAFE_INTEGER) {
|
|
12
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < min || value > max)
|
|
13
|
+
fail("invalid_request", "Invalid integer");
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
export function text(value, max = 256) {
|
|
17
|
+
if (typeof value !== "string" || !value.length || value.length > max)
|
|
18
|
+
fail("invalid_request", "Invalid string");
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
export function canonical(value) {
|
|
22
|
+
let nodes = 0;
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
function visit(v, depth) {
|
|
25
|
+
if (++nodes > 100_000 || depth > 64)
|
|
26
|
+
fail("invalid_request", "JSON is too complex");
|
|
27
|
+
if (v === null || typeof v === "boolean" || typeof v === "string")
|
|
28
|
+
return v;
|
|
29
|
+
if (typeof v === "number" && Number.isFinite(v))
|
|
30
|
+
return v;
|
|
31
|
+
if (typeof v !== "object" || seen.has(v))
|
|
32
|
+
fail("invalid_request", "Expected finite, acyclic JSON");
|
|
33
|
+
if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype && Object.getPrototypeOf(v) !== null)
|
|
34
|
+
fail("invalid_request", "Expected plain JSON");
|
|
35
|
+
seen.add(v);
|
|
36
|
+
const out = Array.isArray(v) ? v.map(x => visit(x, depth + 1)) : Object.fromEntries(Object.keys(v).sort().map(k => [k, visit(v[k], depth + 1)]));
|
|
37
|
+
seen.delete(v);
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
return JSON.stringify(visit(value, 0));
|
|
41
|
+
}
|
|
42
|
+
export function digest(value) { return createHash("sha256").update(canonical(value)).digest("hex"); }
|
|
43
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/contract/validation.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./contract/persistence.types.js";
|
|
2
|
+
export * from "./contract/persistence.store.js";
|
|
3
|
+
export * from "./contract/errors.js";
|
|
4
|
+
export * from "./contract/persistence.protocol.js";
|
|
5
|
+
export * from "./sqlite/sqlite.persistence.store.js";
|
|
6
|
+
export * from "./server/declaration.js";
|
|
7
|
+
export * from "./server/persistence.behavior.js";
|
|
8
|
+
export * from "./server/persistence.slot.store.js";
|
|
9
|
+
export * from "./provider/artifact.http.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./contract/persistence.types.js";
|
|
2
|
+
export * from "./contract/persistence.store.js";
|
|
3
|
+
export * from "./contract/errors.js";
|
|
4
|
+
export * from "./contract/persistence.protocol.js";
|
|
5
|
+
export * from "./sqlite/sqlite.persistence.store.js";
|
|
6
|
+
export * from "./server/declaration.js";
|
|
7
|
+
export * from "./server/persistence.behavior.js";
|
|
8
|
+
export * from "./server/persistence.slot.store.js";
|
|
9
|
+
export * from "./provider/artifact.http.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iCAAiC,CAAC;AAChD,cAAc,iCAAiC,CAAC;AAChD,cAAc,sBAAsB,CAAC;AACrC,cAAc,oCAAoC,CAAC;AACnD,cAAc,sCAAsC,CAAC;AACrD,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,oCAAoC,CAAC;AACnD,cAAc,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Server } from "node:http";
|
|
2
|
+
import type { IPersistenceStore } from "../contract/persistence.store.js";
|
|
3
|
+
/** Every stream is authorized by a fresh MCP call made with the HTTP caller's bearer. */
|
|
4
|
+
export declare function createArtifactTransferServer(options: {
|
|
5
|
+
store: IPersistenceStore;
|
|
6
|
+
brokerMcpUrl: string;
|
|
7
|
+
dev?: boolean;
|
|
8
|
+
}): Server;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { Readable } from "node:stream";
|
|
3
|
+
import { pipeline } from "node:stream/promises";
|
|
4
|
+
import { McpClient } from "@cyanmycelium/mcp-core";
|
|
5
|
+
import { StreamableHttpTransport } from "@cyanmycelium/mcp-core/node";
|
|
6
|
+
import { PersistenceError } from "../contract/errors.js";
|
|
7
|
+
import { segment } from "../contract/validation.js";
|
|
8
|
+
import { MCP_STORE_CONTEXT } from "../server/persistence.behavior.js";
|
|
9
|
+
import { slotPayload } from "../server/persistence.slot.store.js";
|
|
10
|
+
/** Every stream is authorized by a fresh MCP call made with the HTTP caller's bearer. */
|
|
11
|
+
export function createArtifactTransferServer(options) {
|
|
12
|
+
const server = createServer(async (request, response) => {
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
request.once("aborted", () => controller.abort());
|
|
15
|
+
response.once("close", () => { if (!response.writableFinished)
|
|
16
|
+
controller.abort(); });
|
|
17
|
+
let client;
|
|
18
|
+
try {
|
|
19
|
+
const path = new URL(request.url ?? "/", "http://localhost").pathname;
|
|
20
|
+
const match = /^\/(uploads|artifacts)\/([^/]+)\/([^/]+)$/.exec(path);
|
|
21
|
+
if (!match || !((match[1] === "uploads" && request.method === "PUT") || (match[1] === "artifacts" && request.method === "GET"))) {
|
|
22
|
+
response.writeHead(404).end();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const namespace = segment(match[2]), id = segment(match[3]);
|
|
26
|
+
const auth = request.headers.authorization;
|
|
27
|
+
if (!options.dev && (!auth || !/^Bearer [^\s]+$/i.test(auth)))
|
|
28
|
+
throw new PersistenceError("policy_denied", "Bearer authorization required");
|
|
29
|
+
client = new McpClient({ name: "persistence-transfer", version: "0.1.0" }, new StreamableHttpTransport(options.brokerMcpUrl, { headers: auth ? { Authorization: auth } : {} }), 15_000);
|
|
30
|
+
let permission;
|
|
31
|
+
try {
|
|
32
|
+
await client.connect();
|
|
33
|
+
permission = slotPayload(await client.callTool(match[1] === "uploads" ? "persistence.uploadAccess" : "persistence.getArtifact", { namespace, ...(match[1] === "uploads" ? { uploadId: id } : { id }) }));
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error instanceof PersistenceError)
|
|
37
|
+
throw error;
|
|
38
|
+
throw new PersistenceError("policy_denied", "Broker did not authorize transfer");
|
|
39
|
+
}
|
|
40
|
+
const context = { ...MCP_STORE_CONTEXT, signal: controller.signal };
|
|
41
|
+
if (match[1] === "uploads") {
|
|
42
|
+
await options.store.writeArtifactAsync({ namespace, uploadId: id, bytes: request }, context);
|
|
43
|
+
response.writeHead(204).end();
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const metadata = permission;
|
|
47
|
+
const bytes = await options.store.readArtifactAsync({ namespace, id }, context);
|
|
48
|
+
response.writeHead(200, { "Content-Type": metadata.mimeType, "Content-Length": metadata.size, "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff" });
|
|
49
|
+
await pipeline(Readable.from(bytes), response, { signal: controller.signal });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (response.headersSent)
|
|
54
|
+
response.destroy();
|
|
55
|
+
else {
|
|
56
|
+
const code = error instanceof PersistenceError ? error.code : "store_unavailable";
|
|
57
|
+
const status = code === "policy_denied" ? 403 : code === "not_found" ? 404 : code === "quota_exceeded" ? 413 : ["invalid_request", "transfer_incomplete", "integrity_error"].includes(code) ? 400 : 500;
|
|
58
|
+
response.writeHead(status, { "Content-Type": "application/json", "Cache-Control": "no-store" }).end(JSON.stringify({ error: { code, message: "Artifact transfer refused or failed" } }));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
client?.disconnect();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
server.requestTimeout = 30 * 60_000;
|
|
66
|
+
server.headersTimeout = 15_000;
|
|
67
|
+
return server;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=artifact.http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact.http.js","sourceRoot":"","sources":["../../src/provider/artifact.http.ts"],"names":[],"mappings":"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"}
|