mrmainspring 0.1.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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/dist/audit/service.d.ts +7 -0
  4. package/dist/audit/service.js +98 -0
  5. package/dist/audit/store.d.ts +7 -0
  6. package/dist/audit/store.js +37 -0
  7. package/dist/audit/supabase-store.d.ts +9 -0
  8. package/dist/audit/supabase-store.js +22 -0
  9. package/dist/audit/types.d.ts +31 -0
  10. package/dist/audit/types.js +1 -0
  11. package/dist/casper/anchorClient.d.ts +99 -0
  12. package/dist/casper/anchorClient.js +412 -0
  13. package/dist/config.d.ts +51 -0
  14. package/dist/config.js +215 -0
  15. package/dist/env-file.d.ts +1 -0
  16. package/dist/env-file.js +51 -0
  17. package/dist/grimoire/service.d.ts +13 -0
  18. package/dist/grimoire/service.js +199 -0
  19. package/dist/grimoire/store.d.ts +10 -0
  20. package/dist/grimoire/store.js +64 -0
  21. package/dist/grimoire/supabase-store.d.ts +13 -0
  22. package/dist/grimoire/supabase-store.js +50 -0
  23. package/dist/grimoire/types.d.ts +60 -0
  24. package/dist/grimoire/types.js +1 -0
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.js +17 -0
  27. package/dist/mcp/auditTools.d.ts +3 -0
  28. package/dist/mcp/auditTools.js +13 -0
  29. package/dist/mcp/grimoireTools.d.ts +3 -0
  30. package/dist/mcp/grimoireTools.js +91 -0
  31. package/dist/mcp/jsonResult.d.ts +2 -0
  32. package/dist/mcp/jsonResult.js +10 -0
  33. package/dist/mcp/memoryTools.d.ts +3 -0
  34. package/dist/mcp/memoryTools.js +73 -0
  35. package/dist/mcp/paymentTools.d.ts +3 -0
  36. package/dist/mcp/paymentTools.js +33 -0
  37. package/dist/memory/canonical.d.ts +4 -0
  38. package/dist/memory/canonical.js +49 -0
  39. package/dist/memory/hash.d.ts +1 -0
  40. package/dist/memory/hash.js +4 -0
  41. package/dist/memory/service.d.ts +37 -0
  42. package/dist/memory/service.js +175 -0
  43. package/dist/memory/store.d.ts +8 -0
  44. package/dist/memory/store.js +49 -0
  45. package/dist/memory/supabase-store.d.ts +10 -0
  46. package/dist/memory/supabase-store.js +30 -0
  47. package/dist/memory/types.d.ts +56 -0
  48. package/dist/memory/types.js +7 -0
  49. package/dist/payments/service.d.ts +26 -0
  50. package/dist/payments/service.js +613 -0
  51. package/dist/payments/store.d.ts +10 -0
  52. package/dist/payments/store.js +64 -0
  53. package/dist/payments/supabase-store.d.ts +13 -0
  54. package/dist/payments/supabase-store.js +51 -0
  55. package/dist/payments/types.d.ts +101 -0
  56. package/dist/payments/types.js +1 -0
  57. package/dist/server.d.ts +5 -0
  58. package/dist/server.js +68 -0
  59. package/dist/storage/json-file-store.d.ts +17 -0
  60. package/dist/storage/json-file-store.js +87 -0
  61. package/dist/storage/store-factory.d.ts +12 -0
  62. package/dist/storage/store-factory.js +26 -0
  63. package/dist/storage/supabase-rest.d.ts +26 -0
  64. package/dist/storage/supabase-rest.js +85 -0
  65. package/dist/x402/client.d.ts +44 -0
  66. package/dist/x402/client.js +95 -0
  67. package/dist/x402/facilitator.d.ts +84 -0
  68. package/dist/x402/facilitator.js +800 -0
  69. package/dist/x402/readiness.d.ts +55 -0
  70. package/dist/x402/readiness.js +433 -0
  71. package/dist/x402/redaction.d.ts +1 -0
  72. package/dist/x402/redaction.js +30 -0
  73. package/dist/x402/resource.d.ts +69 -0
  74. package/dist/x402/resource.js +325 -0
  75. package/dist/x402/settlement.d.ts +176 -0
  76. package/dist/x402/settlement.js +1210 -0
  77. package/dist/x402/signer.d.ts +71 -0
  78. package/dist/x402/signer.js +616 -0
  79. package/package.json +61 -0
@@ -0,0 +1,51 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ export function loadLocalEnvFile(env = process.env) {
5
+ const envPath = resolveEnvPath(env);
6
+ if (!envPath || !existsSync(envPath)) {
7
+ return null;
8
+ }
9
+ const raw = readFileSync(envPath, "utf8");
10
+ for (const [key, value] of parseEnv(raw)) {
11
+ if (env[key] === undefined) {
12
+ env[key] = value;
13
+ }
14
+ }
15
+ return envPath;
16
+ }
17
+ function resolveEnvPath(env) {
18
+ if (env.SIGIL_ENV_FILE?.trim()) {
19
+ return env.SIGIL_ENV_FILE.trim();
20
+ }
21
+ const backendRoot = dirname(dirname(fileURLToPath(import.meta.url)));
22
+ const repoRoot = dirname(backendRoot);
23
+ const candidates = [join(process.cwd(), ".env"), join(backendRoot, ".env"), join(repoRoot, ".env")];
24
+ return candidates.find((candidate) => existsSync(candidate)) ?? candidates.at(-1) ?? null;
25
+ }
26
+ function parseEnv(raw) {
27
+ const entries = [];
28
+ for (const line of raw.split(/\r?\n/)) {
29
+ const trimmed = line.trim();
30
+ if (!trimmed || trimmed.startsWith("#")) {
31
+ continue;
32
+ }
33
+ const separator = trimmed.indexOf("=");
34
+ if (separator <= 0) {
35
+ continue;
36
+ }
37
+ const key = trimmed.slice(0, separator).trim();
38
+ const value = unquote(trimmed.slice(separator + 1).trim());
39
+ if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
40
+ entries.push([key, value]);
41
+ }
42
+ }
43
+ return entries;
44
+ }
45
+ function unquote(value) {
46
+ if ((value.startsWith('"') && value.endsWith('"')) ||
47
+ (value.startsWith("'") && value.endsWith("'"))) {
48
+ return value.slice(1, -1);
49
+ }
50
+ return value;
51
+ }
@@ -0,0 +1,13 @@
1
+ import type { AuditService } from "../audit/service.js";
2
+ import type { GrimoireStore, PolicyRecord, PolicySetInput, SecretMetadata, SecretPutInput } from "./types.js";
3
+ export declare class GrimoireService {
4
+ private readonly store;
5
+ private readonly masterKey;
6
+ private readonly audit?;
7
+ constructor(store: GrimoireStore, masterKey: Buffer, audit?: AuditService | undefined);
8
+ putSecret(input: SecretPutInput): Promise<SecretMetadata>;
9
+ listSecrets(agentId: string): Promise<SecretMetadata[]>;
10
+ setPolicy(input: PolicySetInput): Promise<PolicyRecord>;
11
+ getPolicy(agentId: string, policyId: string): Promise<PolicyRecord | null>;
12
+ recordPolicySpend(agentId: string, policyId: string, amount: string): Promise<PolicyRecord | null>;
13
+ }
@@ -0,0 +1,199 @@
1
+ import { createCipheriv, randomBytes, randomUUID } from "node:crypto";
2
+ import { canonicalizeJson, toJsonObject } from "../memory/canonical.js";
3
+ import { sha256Hex } from "../memory/hash.js";
4
+ export class GrimoireService {
5
+ store;
6
+ masterKey;
7
+ audit;
8
+ constructor(store, masterKey, audit) {
9
+ this.store = store;
10
+ this.masterKey = masterKey;
11
+ this.audit = audit;
12
+ }
13
+ async putSecret(input) {
14
+ const now = new Date().toISOString();
15
+ const encrypted = encrypt(input.value, this.masterKey);
16
+ const existing = await this.store.getSecretByName(input.agent_id, input.name);
17
+ const record = {
18
+ id: existing?.id ?? createId("sec"),
19
+ agent_id: input.agent_id,
20
+ name: input.name,
21
+ type: input.type,
22
+ scopes: [...new Set(input.scopes)].sort(),
23
+ ciphertext: encrypted.ciphertext,
24
+ nonce: encrypted.nonce,
25
+ auth_tag: encrypted.authTag,
26
+ key_version: "local-v1",
27
+ created_at: existing?.created_at ?? now,
28
+ updated_at: now,
29
+ deleted_at: null
30
+ };
31
+ await this.store.saveSecret(record);
32
+ await this.audit?.record({
33
+ agent_id: record.agent_id,
34
+ event_type: "secret.stored",
35
+ subject_type: "secret",
36
+ subject_id: record.id,
37
+ metadata: {
38
+ name: record.name,
39
+ type: record.type,
40
+ scopes: record.scopes
41
+ }
42
+ });
43
+ return toSecretMetadata(record);
44
+ }
45
+ async listSecrets(agentId) {
46
+ const secrets = await this.store.listSecrets(agentId);
47
+ const metadata = secrets.filter((secret) => !secret.deleted_at).map(toSecretMetadata);
48
+ await this.audit?.record({
49
+ agent_id: agentId,
50
+ event_type: "secret.listed",
51
+ subject_type: "secret",
52
+ metadata: { count: metadata.length }
53
+ });
54
+ return metadata;
55
+ }
56
+ async setPolicy(input) {
57
+ assertDecimalAmount("max_amount_per_call", input.max_amount_per_call);
58
+ assertDecimalAmount("max_amount_per_period", input.max_amount_per_period);
59
+ const now = new Date().toISOString();
60
+ const existing = await this.store.getPolicy(input.agent_id, input.policy_id);
61
+ const policyBody = {
62
+ agent_id: input.agent_id,
63
+ policy_id: input.policy_id,
64
+ enabled: input.enabled ?? true,
65
+ allowed_urls: input.allowed_urls,
66
+ allowed_methods: input.allowed_methods.map((method) => method.toUpperCase()).sort(),
67
+ allowed_asset: toJsonObject(input.allowed_asset, "allowed_asset"),
68
+ max_amount_per_call: input.max_amount_per_call,
69
+ max_amount_per_period: input.max_amount_per_period,
70
+ period_seconds: input.period_seconds,
71
+ secret_scopes: [...new Set(input.secret_scopes)].sort()
72
+ };
73
+ const record = {
74
+ ...policyBody,
75
+ policy_hash: sha256Hex(canonicalizeJson(policyBody)),
76
+ current_period_spend: existing?.current_period_spend ?? "0",
77
+ period_started_at: existing?.period_started_at ?? now,
78
+ created_at: existing?.created_at ?? now,
79
+ updated_at: now
80
+ };
81
+ await this.store.savePolicy(record);
82
+ await this.audit?.record({
83
+ agent_id: record.agent_id,
84
+ event_type: "policy.set",
85
+ subject_type: "policy",
86
+ subject_id: record.policy_id,
87
+ metadata: {
88
+ enabled: record.enabled,
89
+ policy_hash: record.policy_hash,
90
+ allowed_urls: record.allowed_urls,
91
+ allowed_methods: record.allowed_methods
92
+ }
93
+ });
94
+ return record;
95
+ }
96
+ async getPolicy(agentId, policyId) {
97
+ const policy = await this.store.getPolicy(agentId, policyId);
98
+ await this.audit?.record({
99
+ agent_id: agentId,
100
+ event_type: "policy.get",
101
+ subject_type: "policy",
102
+ subject_id: policyId,
103
+ metadata: { found: Boolean(policy) }
104
+ });
105
+ return policy;
106
+ }
107
+ async recordPolicySpend(agentId, policyId, amount) {
108
+ assertDecimalAmount("amount", amount);
109
+ const policy = await this.store.getPolicy(agentId, policyId);
110
+ if (!policy) {
111
+ await this.audit?.record({
112
+ agent_id: agentId,
113
+ event_type: "policy.spend_record_failed",
114
+ subject_type: "policy",
115
+ subject_id: policyId,
116
+ severity: "warn",
117
+ metadata: { reason: "policy_not_found" }
118
+ });
119
+ return null;
120
+ }
121
+ const now = new Date();
122
+ const nowIso = now.toISOString();
123
+ const periodStartedAt = Date.parse(policy.period_started_at);
124
+ const periodExpired = Number.isNaN(periodStartedAt) ||
125
+ now.getTime() - periodStartedAt >= policy.period_seconds * 1000;
126
+ const currentSpend = periodExpired ? "0" : policy.current_period_spend;
127
+ const updated = {
128
+ ...policy,
129
+ current_period_spend: addDecimalAmounts(currentSpend, amount),
130
+ period_started_at: periodExpired ? nowIso : policy.period_started_at,
131
+ updated_at: nowIso
132
+ };
133
+ await this.store.savePolicy(updated);
134
+ await this.audit?.record({
135
+ agent_id: agentId,
136
+ event_type: "policy.spend_recorded",
137
+ subject_type: "policy",
138
+ subject_id: policyId,
139
+ metadata: {
140
+ amount,
141
+ current_period_spend: updated.current_period_spend,
142
+ policy_hash: updated.policy_hash
143
+ }
144
+ });
145
+ return updated;
146
+ }
147
+ }
148
+ function encrypt(value, key) {
149
+ const nonce = randomBytes(12);
150
+ const cipher = createCipheriv("aes-256-gcm", key, nonce);
151
+ const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
152
+ const authTag = cipher.getAuthTag();
153
+ return {
154
+ ciphertext: ciphertext.toString("base64"),
155
+ nonce: nonce.toString("base64"),
156
+ authTag: authTag.toString("base64")
157
+ };
158
+ }
159
+ function toSecretMetadata(secret) {
160
+ return {
161
+ id: secret.id,
162
+ agent_id: secret.agent_id,
163
+ name: secret.name,
164
+ type: secret.type,
165
+ scopes: secret.scopes,
166
+ created_at: secret.created_at,
167
+ updated_at: secret.updated_at
168
+ };
169
+ }
170
+ function createId(prefix) {
171
+ return `${prefix}_${randomUUID().replaceAll("-", "")}`;
172
+ }
173
+ function assertDecimalAmount(name, value) {
174
+ if (!/^\d+(\.\d+)?$/.test(value)) {
175
+ throw new Error(`${name} must be a non-negative decimal amount`);
176
+ }
177
+ }
178
+ function addDecimalAmounts(left, right) {
179
+ const leftParts = parseDecimalAmount(left);
180
+ const rightParts = parseDecimalAmount(right);
181
+ const scale = Math.max(leftParts.scale, rightParts.scale);
182
+ const leftValue = leftParts.value * 10n ** BigInt(scale - leftParts.scale);
183
+ const rightValue = rightParts.value * 10n ** BigInt(scale - rightParts.scale);
184
+ const sum = (leftValue + rightValue).toString().padStart(scale + 1, "0");
185
+ if (scale === 0) {
186
+ return sum;
187
+ }
188
+ const whole = sum.slice(0, -scale);
189
+ const fraction = sum.slice(-scale).replace(/0+$/, "");
190
+ return fraction ? `${whole}.${fraction}` : whole;
191
+ }
192
+ function parseDecimalAmount(value) {
193
+ assertDecimalAmount("amount", value);
194
+ const [whole, fraction = ""] = value.split(".");
195
+ return {
196
+ value: BigInt(`${whole}${fraction}`),
197
+ scale: fraction.length
198
+ };
199
+ }
@@ -0,0 +1,10 @@
1
+ import type { GrimoireStore, PolicyRecord, SecretRecord } from "./types.js";
2
+ export declare class FileGrimoireStore implements GrimoireStore {
3
+ private readonly store;
4
+ constructor(dataDir: string);
5
+ saveSecret(secret: SecretRecord): Promise<void>;
6
+ getSecretByName(agentId: string, name: string): Promise<SecretRecord | null>;
7
+ listSecrets(agentId: string): Promise<SecretRecord[]>;
8
+ savePolicy(policy: PolicyRecord): Promise<void>;
9
+ getPolicy(agentId: string, policyId: string): Promise<PolicyRecord | null>;
10
+ }
@@ -0,0 +1,64 @@
1
+ import { join } from "node:path";
2
+ import { JsonFileStore } from "../storage/json-file-store.js";
3
+ export class FileGrimoireStore {
4
+ store;
5
+ constructor(dataDir) {
6
+ this.store = new JsonFileStore({
7
+ filePath: join(dataDir, "grimoire.json"),
8
+ empty: emptyStore,
9
+ normalize: normalizeStore
10
+ });
11
+ }
12
+ async saveSecret(secret) {
13
+ await this.store.update((data) => {
14
+ const existingIndex = data.secrets.findIndex((item) => item.id === secret.id);
15
+ if (existingIndex >= 0) {
16
+ data.secrets[existingIndex] = secret;
17
+ }
18
+ else {
19
+ data.secrets.push(secret);
20
+ }
21
+ });
22
+ }
23
+ async getSecretByName(agentId, name) {
24
+ const data = await this.store.read();
25
+ return (data.secrets.find((secret) => secret.agent_id === agentId && secret.name === name && !secret.deleted_at) ?? null);
26
+ }
27
+ async listSecrets(agentId) {
28
+ const data = await this.store.read();
29
+ return data.secrets.filter((secret) => secret.agent_id === agentId);
30
+ }
31
+ async savePolicy(policy) {
32
+ await this.store.update((data) => {
33
+ const existingIndex = data.policies.findIndex((item) => item.agent_id === policy.agent_id && item.policy_id === policy.policy_id);
34
+ if (existingIndex >= 0) {
35
+ data.policies[existingIndex] = policy;
36
+ }
37
+ else {
38
+ data.policies.push(policy);
39
+ }
40
+ });
41
+ }
42
+ async getPolicy(agentId, policyId) {
43
+ const data = await this.store.read();
44
+ return (data.policies.find((policy) => policy.agent_id === agentId && policy.policy_id === policyId) ?? null);
45
+ }
46
+ }
47
+ function emptyStore() {
48
+ return {
49
+ schema_version: "sigil.grimoire-store.v1",
50
+ secrets: [],
51
+ policies: []
52
+ };
53
+ }
54
+ function normalizeStore(parsed) {
55
+ const data = asStoreObject(parsed);
56
+ return {
57
+ schema_version: "sigil.grimoire-store.v1",
58
+ secrets: Array.isArray(data.secrets) ? data.secrets : [],
59
+ policies: Array.isArray(data.policies) ? data.policies : []
60
+ };
61
+ }
62
+ function asStoreObject(parsed) {
63
+ return parsed && typeof parsed === "object" ? parsed : {};
64
+ }
@@ -0,0 +1,13 @@
1
+ import { SupabaseRestClient } from "../storage/supabase-rest.js";
2
+ import type { GrimoireStore, PolicyRecord, SecretRecord } from "./types.js";
3
+ export declare class SupabaseGrimoireStore implements GrimoireStore {
4
+ private readonly client;
5
+ private readonly secretsTable;
6
+ private readonly policiesTable;
7
+ constructor(client: SupabaseRestClient);
8
+ saveSecret(secret: SecretRecord): Promise<void>;
9
+ getSecretByName(agentId: string, name: string): Promise<SecretRecord | null>;
10
+ listSecrets(agentId: string): Promise<SecretRecord[]>;
11
+ savePolicy(policy: PolicyRecord): Promise<void>;
12
+ getPolicy(agentId: string, policyId: string): Promise<PolicyRecord | null>;
13
+ }
@@ -0,0 +1,50 @@
1
+ export class SupabaseGrimoireStore {
2
+ client;
3
+ secretsTable;
4
+ policiesTable;
5
+ constructor(client) {
6
+ this.client = client;
7
+ this.secretsTable = client.table("secrets");
8
+ this.policiesTable = client.table("policies");
9
+ }
10
+ async saveSecret(secret) {
11
+ await this.client.upsert(this.secretsTable, {
12
+ id: secret.id,
13
+ agent_id: secret.agent_id,
14
+ name: secret.name,
15
+ created_at: secret.created_at,
16
+ updated_at: secret.updated_at,
17
+ deleted_at: secret.deleted_at,
18
+ record: secret
19
+ }, "id");
20
+ }
21
+ async getSecretByName(agentId, name) {
22
+ const records = await this.client.selectRecords(this.secretsTable, {
23
+ filters: { agent_id: agentId, name },
24
+ order: { column: "created_at" }
25
+ });
26
+ return records.find((secret) => !secret.deleted_at) ?? null;
27
+ }
28
+ async listSecrets(agentId) {
29
+ return this.client.selectRecords(this.secretsTable, {
30
+ filters: { agent_id: agentId },
31
+ order: { column: "created_at" }
32
+ });
33
+ }
34
+ async savePolicy(policy) {
35
+ await this.client.upsert(this.policiesTable, {
36
+ agent_id: policy.agent_id,
37
+ policy_id: policy.policy_id,
38
+ created_at: policy.created_at,
39
+ updated_at: policy.updated_at,
40
+ record: policy
41
+ }, "agent_id,policy_id");
42
+ }
43
+ async getPolicy(agentId, policyId) {
44
+ const [record] = await this.client.selectRecords(this.policiesTable, {
45
+ filters: { agent_id: agentId, policy_id: policyId },
46
+ limit: 1
47
+ });
48
+ return record ?? null;
49
+ }
50
+ }
@@ -0,0 +1,60 @@
1
+ import type { JsonObject } from "../memory/types.js";
2
+ export type SecretType = "casper_private_key_ref" | "x402_client_key_ref" | "api_key" | "webhook_secret";
3
+ export type SecretPutInput = {
4
+ agent_id: string;
5
+ name: string;
6
+ type: SecretType;
7
+ value: string;
8
+ scopes: string[];
9
+ };
10
+ export type SecretRecord = {
11
+ id: string;
12
+ agent_id: string;
13
+ name: string;
14
+ type: SecretType;
15
+ scopes: string[];
16
+ ciphertext: string;
17
+ nonce: string;
18
+ auth_tag: string;
19
+ key_version: string;
20
+ created_at: string;
21
+ updated_at: string;
22
+ deleted_at: string | null;
23
+ };
24
+ export type SecretMetadata = Pick<SecretRecord, "id" | "agent_id" | "name" | "type" | "scopes" | "created_at" | "updated_at">;
25
+ export type PolicySetInput = {
26
+ agent_id: string;
27
+ policy_id: string;
28
+ enabled?: boolean;
29
+ allowed_urls: string[];
30
+ allowed_methods: string[];
31
+ allowed_asset: unknown;
32
+ max_amount_per_call: string;
33
+ max_amount_per_period: string;
34
+ period_seconds: number;
35
+ secret_scopes: string[];
36
+ };
37
+ export type PolicyRecord = {
38
+ agent_id: string;
39
+ policy_id: string;
40
+ enabled: boolean;
41
+ allowed_urls: string[];
42
+ allowed_methods: string[];
43
+ allowed_asset: JsonObject;
44
+ max_amount_per_call: string;
45
+ max_amount_per_period: string;
46
+ period_seconds: number;
47
+ secret_scopes: string[];
48
+ policy_hash: string;
49
+ current_period_spend: string;
50
+ period_started_at: string;
51
+ created_at: string;
52
+ updated_at: string;
53
+ };
54
+ export type GrimoireStore = {
55
+ saveSecret(secret: SecretRecord): Promise<void>;
56
+ getSecretByName(agentId: string, name: string): Promise<SecretRecord | null>;
57
+ listSecrets(agentId: string): Promise<SecretRecord[]>;
58
+ savePolicy(policy: PolicyRecord): Promise<void>;
59
+ getPolicy(agentId: string, policyId: string): Promise<PolicyRecord | null>;
60
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { loadConfig } from "./config.js";
4
+ import { loadLocalEnvFile } from "./env-file.js";
5
+ import { createSigilServer } from "./server.js";
6
+ async function main() {
7
+ loadLocalEnvFile();
8
+ const config = loadConfig();
9
+ const server = createSigilServer(config);
10
+ const transport = new StdioServerTransport();
11
+ await server.connect(transport);
12
+ }
13
+ main().catch((error) => {
14
+ const message = error instanceof Error ? error.stack ?? error.message : String(error);
15
+ console.error(message);
16
+ process.exitCode = 1;
17
+ });
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { AuditService } from "../audit/service.js";
3
+ export declare function registerAuditTools(server: McpServer, auditService: AuditService): void;
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ import { jsonResult } from "./jsonResult.js";
3
+ export function registerAuditTools(server, auditService) {
4
+ server.registerTool("audit.tail", {
5
+ title: "Tail Audit Events",
6
+ description: "Return recent Mr Mainspring audit events for memory, Grimoire, payment, and anchor activity.",
7
+ inputSchema: {
8
+ agent_id: z.string().min(1).optional(),
9
+ event_type: z.string().min(1).optional(),
10
+ limit: z.number().int().min(1).max(200).optional()
11
+ }
12
+ }, async (input) => jsonResult(await auditService.tail(input)));
13
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { GrimoireService } from "../grimoire/service.js";
3
+ export declare function registerGrimoireTools(server: McpServer, grimoireService: GrimoireService): void;
@@ -0,0 +1,91 @@
1
+ import { z } from "zod";
2
+ import { jsonResult } from "./jsonResult.js";
3
+ const secretTypeSchema = z.enum([
4
+ "casper_private_key_ref",
5
+ "x402_client_key_ref",
6
+ "api_key",
7
+ "webhook_secret"
8
+ ]);
9
+ const jsonObjectSchema = z.record(z.string(), z.unknown());
10
+ const nonEmptyStringSchema = z.string().trim().min(1);
11
+ const urlSchema = z.string().trim().url();
12
+ const decimalAmountSchema = z
13
+ .string()
14
+ .trim()
15
+ .regex(/^\d+(\.\d+)?$/, "Expected a non-negative decimal amount");
16
+ export function registerGrimoireTools(server, grimoireService) {
17
+ server.registerTool("grimoire.secret.put", {
18
+ title: "Store Secret",
19
+ description: "Encrypt and store a scoped Mr Mainspring secret. The plaintext is never returned.",
20
+ inputSchema: {
21
+ agent_id: nonEmptyStringSchema,
22
+ name: nonEmptyStringSchema,
23
+ type: secretTypeSchema,
24
+ value: nonEmptyStringSchema,
25
+ scopes: z.array(nonEmptyStringSchema).min(1)
26
+ }
27
+ }, async (input) => {
28
+ const secret = await grimoireService.putSecret(input);
29
+ return jsonResult({
30
+ status: "stored",
31
+ secret
32
+ });
33
+ });
34
+ server.registerTool("grimoire.secret.list", {
35
+ title: "List Secrets",
36
+ description: "List Mr Mainspring secret metadata for an agent without exposing secret values.",
37
+ inputSchema: {
38
+ agent_id: nonEmptyStringSchema
39
+ }
40
+ }, async ({ agent_id }) => {
41
+ const secrets = await grimoireService.listSecrets(agent_id);
42
+ return jsonResult({
43
+ agent_id,
44
+ count: secrets.length,
45
+ secrets
46
+ });
47
+ });
48
+ server.registerTool("grimoire.policy.set", {
49
+ title: "Set Policy",
50
+ description: "Create or update a Mr Mainspring spending/access policy commitment.",
51
+ inputSchema: {
52
+ agent_id: nonEmptyStringSchema,
53
+ policy_id: nonEmptyStringSchema,
54
+ enabled: z.boolean().optional(),
55
+ allowed_urls: z.array(urlSchema).min(1),
56
+ allowed_methods: z.array(nonEmptyStringSchema).min(1),
57
+ allowed_asset: jsonObjectSchema,
58
+ max_amount_per_call: decimalAmountSchema,
59
+ max_amount_per_period: decimalAmountSchema,
60
+ period_seconds: z.number().int().positive(),
61
+ secret_scopes: z.array(nonEmptyStringSchema).default([])
62
+ }
63
+ }, async (input) => {
64
+ const policy = await grimoireService.setPolicy(input);
65
+ return jsonResult({
66
+ status: "stored",
67
+ policy
68
+ });
69
+ });
70
+ server.registerTool("grimoire.policy.get", {
71
+ title: "Get Policy",
72
+ description: "Read one Mr Mainspring policy and current local spend metadata.",
73
+ inputSchema: {
74
+ agent_id: nonEmptyStringSchema,
75
+ policy_id: nonEmptyStringSchema
76
+ }
77
+ }, async ({ agent_id, policy_id }) => {
78
+ const policy = await grimoireService.getPolicy(agent_id, policy_id);
79
+ if (!policy) {
80
+ return jsonResult({
81
+ found: false,
82
+ agent_id,
83
+ policy_id
84
+ });
85
+ }
86
+ return jsonResult({
87
+ found: true,
88
+ policy
89
+ });
90
+ });
91
+ }
@@ -0,0 +1,2 @@
1
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ export declare function jsonResult(data: unknown): CallToolResult;
@@ -0,0 +1,10 @@
1
+ export function jsonResult(data) {
2
+ return {
3
+ content: [
4
+ {
5
+ type: "text",
6
+ text: JSON.stringify(data, null, 2)
7
+ }
8
+ ]
9
+ };
10
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { MemoryService } from "../memory/service.js";
3
+ export declare function registerMemoryTools(server: McpServer, memoryService: MemoryService): void;