@crediolabs/policy-builder-mcp 0.1.6 → 0.1.7
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 +21 -0
- package/README.md +7 -2
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +2 -2
- package/dist/src/schemas.d.ts +58 -2058
- package/dist/src/schemas.js +26 -213
- package/dist/src/server.js +3 -3
- package/dist-cjs/package.json +3 -0
- package/dist-cjs/src/index.d.ts +7 -0
- package/dist-cjs/src/index.js +20 -0
- package/dist-cjs/src/schemas.d.ts +447 -0
- package/dist-cjs/src/schemas.js +58 -0
- package/dist-cjs/src/server.d.ts +6 -0
- package/dist-cjs/src/server.js +42 -0
- package/dist-cjs/src/tools/result.d.ts +24 -0
- package/dist-cjs/src/tools/result.js +39 -0
- package/dist-cjs/src/transports/http.d.ts +15 -0
- package/dist-cjs/src/transports/http.js +140 -0
- package/dist-cjs/src/transports/stdio.d.ts +1 -0
- package/dist-cjs/src/transports/stdio.js +18 -0
- package/package.json +36 -3
- package/src/index.ts +6 -10
- package/src/schemas.ts +47 -239
- package/src/server.ts +3 -3
- package/dist/src/tools/run.d.ts +0 -15
- package/dist/src/tools/run.js +0 -98
- package/src/tools/run.ts +0 -134
package/dist/src/schemas.js
CHANGED
|
@@ -1,152 +1,33 @@
|
|
|
1
1
|
// apps/policy-builder-mcp/src/schemas.ts
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// MCP-only tool shapes. The CORE input/output schemas and the MCP server
|
|
4
|
+
// registrations all live on `@crediolabs/policy-synth` (the tool-body glue
|
|
5
|
+
// sits at `@crediolabs/policy-synth/run`; the underlying Zod input schemas
|
|
6
|
+
// live at `@crediolabs/policy-synth/run`). What stays here is just the flat
|
|
7
|
+
// `ZodRawShape` needed by `@modelcontextprotocol/sdk`'s `tool()` registration
|
|
8
|
+
// API, which does not accept the strict discriminated union `synthesize_policy`
|
|
9
|
+
// needs.
|
|
8
10
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
11
|
+
// The body of every tool call re-validates against the strict schemas via
|
|
12
|
+
// `runRecordTransaction` / `runSynthesizePolicy`, so wire inputs still
|
|
13
|
+
// fail closed. The mutual-exclusion rules (e.g. exactly-one-of hash/xdr)
|
|
14
|
+
// live on the strict schemas, NOT on the tool shape; the SDK does not
|
|
15
|
+
// invoke `.refine()` at registration time, so any refined rule must be
|
|
16
|
+
// re-checked in the body.
|
|
17
|
+
//
|
|
18
|
+
// The tool-shape fields below are hand-rolled simple types rather than
|
|
19
|
+
// references into the refined strict schemas (`.refine()` returns
|
|
20
|
+
// `ZodEffects`, which has no `.shape`). They MUST stay in lockstep with
|
|
21
|
+
// the strict schemas - a drift in field type or optionality breaks the
|
|
22
|
+
// SDK's emitted JSON Schema.
|
|
23
|
+
import { ComposeUserResponsesSchema, InterpreterOptionsSchema, MandateSpecSchema, NetworkSchema, OzAdapterConfigSchema, RecordedTransactionSchema, RecordTransactionInputSchema, SynthesizePolicyInputSchema, ToolErrorSchema, } from '@crediolabs/policy-synth/run';
|
|
13
24
|
import { z } from 'zod';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const MAX_INVOCATIONS = 512;
|
|
21
|
-
export const NetworkSchema = z.enum(['mainnet', 'testnet']);
|
|
22
|
-
/** ScVal subset - normalised subset the synth consumes. Mirrors
|
|
23
|
-
* `ScVal` in packages/policy-synth/src/types.ts. */
|
|
24
|
-
export const ScValSchema = z.lazy(() => z.union([
|
|
25
|
-
z.object({ type: z.literal('address'), value: z.string() }),
|
|
26
|
-
// i128 is SIGNED: real events carry negatives (e.g. a fee-adjustment/refund),
|
|
27
|
-
// so the recorder's own output must round-trip through this schema. u64/u32
|
|
28
|
-
// are unsigned and stay non-negative.
|
|
29
|
-
z.object({ type: z.literal('i128'), value: z.string().regex(/^-?[0-9]+$/) }),
|
|
30
|
-
z.object({ type: z.literal('u64'), value: z.string().regex(/^[0-9]+$/) }),
|
|
31
|
-
z.object({ type: z.literal('u32'), value: z.string().regex(/^[0-9]+$/) }),
|
|
32
|
-
z.object({ type: z.literal('symbol'), value: z.string() }),
|
|
33
|
-
z.object({ type: z.literal('vec'), value: z.array(ScValSchema) }),
|
|
34
|
-
z.object({ type: z.literal('bytes'), value: z.string() }),
|
|
35
|
-
z.object({ type: z.literal('other'), value: z.string() }),
|
|
36
|
-
]));
|
|
37
|
-
/** ContractInvocation mirrors the core. Annotated with an explicit
|
|
38
|
-
* `z.ZodType<unknown>` (like ScValSchema above) so the self-referential
|
|
39
|
-
* `subInvocations` field does not trip TS's circular type inference. */
|
|
40
|
-
export const ContractInvocationSchema = z.object({
|
|
41
|
-
contract: z.string(),
|
|
42
|
-
fn: z.string(),
|
|
43
|
-
args: z.array(ScValSchema),
|
|
44
|
-
subInvocations: z.array(z.lazy(() => ContractInvocationSchema)),
|
|
45
|
-
});
|
|
46
|
-
export const TokenMovementSchema = z.object({
|
|
47
|
-
token: z.string(),
|
|
48
|
-
from: z.string(),
|
|
49
|
-
to: z.string(),
|
|
50
|
-
// The recorder reads the amount straight from the signed i128 event value
|
|
51
|
-
// (record/movements.ts readAmount), so a non-standard token that emits a
|
|
52
|
-
// negative transfer/mint/burn amount round-trips as a negative string. Mirror
|
|
53
|
-
// that here; the synth gate, not the wire schema, decides what to do with it.
|
|
54
|
-
amount: z.string().regex(/^-?[0-9]+$/),
|
|
55
|
-
});
|
|
56
|
-
export const OnChainEventSchema = z.object({
|
|
57
|
-
contract: z.string(),
|
|
58
|
-
topics: z.array(z.string()),
|
|
59
|
-
data: ScValSchema,
|
|
60
|
-
});
|
|
61
|
-
export const ParseConfidenceSchema = z.object({
|
|
62
|
-
overall: z.number().min(0).max(1),
|
|
63
|
-
knownContracts: z.array(z.string()),
|
|
64
|
-
unknownContracts: z.array(z.object({
|
|
65
|
-
contract: z.string(),
|
|
66
|
-
reason: z.enum(['no-abi', 'version-mismatch', 'opaque-result']),
|
|
67
|
-
})),
|
|
68
|
-
opaqueScVals: z.array(z.object({ path: z.string(), type: z.string() })),
|
|
69
|
-
thresholdUsed: z.number().min(0).max(1),
|
|
70
|
-
});
|
|
71
|
-
/** RecordedTransaction mirrors the core RecordedTransaction. The output shape
|
|
72
|
-
* is referenced by name in the tool result structured content; we deliberately
|
|
73
|
-
* type it loosely (`z.unknown()`) on the success path so the core remains the
|
|
74
|
-
* single source of truth for the wire payload. */
|
|
75
|
-
export const RecordedTransactionSchema = z
|
|
76
|
-
.object({
|
|
77
|
-
network: NetworkSchema,
|
|
78
|
-
signers: z.array(z.string()),
|
|
79
|
-
invocations: z.array(ContractInvocationSchema).max(MAX_INVOCATIONS),
|
|
80
|
-
tokenMovements: z.array(TokenMovementSchema),
|
|
81
|
-
events: z.array(OnChainEventSchema),
|
|
82
|
-
authEntries: z.array(z.unknown()),
|
|
83
|
-
ledgerSequence: z.number().int().nonnegative(),
|
|
84
|
-
fetchedAt: z.number().int().nonnegative(),
|
|
85
|
-
parseConfidence: ParseConfidenceSchema,
|
|
86
|
-
sourceAccount: z.string(),
|
|
87
|
-
})
|
|
88
|
-
.passthrough();
|
|
89
|
-
/** MandateSpec mirrors the core MandateSpec. The deterministic Mandate
|
|
90
|
-
* front-end needs no parseConfidence; the tool adapter injects the full
|
|
91
|
-
* confidence after synthesis so the orchestrator can compare. */
|
|
92
|
-
export const MandateSpecSchema = z
|
|
93
|
-
.object({
|
|
94
|
-
chain: z.literal('stellar'),
|
|
95
|
-
contract: z.string(),
|
|
96
|
-
method: z.string().optional(),
|
|
97
|
-
spendingLimit: z
|
|
98
|
-
.object({
|
|
99
|
-
token: z.string(),
|
|
100
|
-
limit: z.string().regex(/^[0-9]+$/),
|
|
101
|
-
windowSeconds: z.number().int().positive(),
|
|
102
|
-
})
|
|
103
|
-
.optional(),
|
|
104
|
-
// A threshold of 0 means "0 approvals", which is not a real M-of-N gate.
|
|
105
|
-
approvalThreshold: z.number().int().positive().optional(),
|
|
106
|
-
recipients: z.array(z.string()).optional(),
|
|
107
|
-
expiry: z
|
|
108
|
-
.object({
|
|
109
|
-
validUntilLedger: z.number().int().positive().max(U32_MAX).optional(),
|
|
110
|
-
validUntilUnixSeconds: z.number().int().positive().optional(),
|
|
111
|
-
})
|
|
112
|
-
.optional(),
|
|
113
|
-
})
|
|
114
|
-
.passthrough();
|
|
115
|
-
/** ComposeUserResponses mirrors the core. */
|
|
116
|
-
export const ComposeUserResponsesSchema = z
|
|
117
|
-
.object({
|
|
118
|
-
windowSeconds: z.number().int().positive().optional(),
|
|
119
|
-
validUntilLedger: z.number().int().positive().max(U32_MAX).optional(),
|
|
120
|
-
limitAmount: z
|
|
121
|
-
.string()
|
|
122
|
-
.regex(/^[0-9]+$/)
|
|
123
|
-
.optional(),
|
|
124
|
-
invocationLimit: z.number().int().positive().optional(),
|
|
125
|
-
})
|
|
126
|
-
.passthrough();
|
|
127
|
-
/** OzAdapterConfig - the per-network OZ built-in instance addresses. */
|
|
128
|
-
export const OzAdapterConfigSchema = z.object({
|
|
129
|
-
network: NetworkSchema,
|
|
130
|
-
instances: z.object({
|
|
131
|
-
spending_limit: z.string(),
|
|
132
|
-
simple_threshold: z.string(),
|
|
133
|
-
weighted_threshold: z.string(),
|
|
134
|
-
}),
|
|
135
|
-
});
|
|
136
|
-
// ===== record_transaction =====
|
|
137
|
-
export const RecordTransactionInputSchema = z
|
|
138
|
-
.object({
|
|
139
|
-
hash: z.string().min(1).optional(),
|
|
140
|
-
xdr: z.string().min(1).optional(),
|
|
141
|
-
network: NetworkSchema,
|
|
142
|
-
confidenceOverride: z.number().min(0).max(1).optional(),
|
|
143
|
-
})
|
|
144
|
-
.refine((v) => !(v.hash && v.xdr), {
|
|
145
|
-
message: 'provide exactly one of `hash` or `xdr`, not both',
|
|
146
|
-
})
|
|
147
|
-
.refine((v) => Boolean(v.hash) || Boolean(v.xdr), {
|
|
148
|
-
message: 'one of `hash` or `xdr` is required',
|
|
149
|
-
});
|
|
25
|
+
// Re-export the strict schemas so MCP package consumers (and existing tests)
|
|
26
|
+
// still get them from this module. The canonical home is
|
|
27
|
+
// `@crediolabs/policy-synth/run`; the re-exports here are a shim kept for
|
|
28
|
+
// backward compatibility with downstream callers that imported from the MCP
|
|
29
|
+
// package directly.
|
|
30
|
+
export { ComposeUserResponsesSchema, InterpreterOptionsSchema, MandateSpecSchema, NetworkSchema, OzAdapterConfigSchema, RecordedTransactionSchema, RecordTransactionInputSchema, SynthesizePolicyInputSchema, ToolErrorSchema, };
|
|
150
31
|
/** Flat ZodRawShape used for the MCP SDK tool registration. The body
|
|
151
32
|
* re-validates against `RecordTransactionInputSchema` so the mutual-exclusion
|
|
152
33
|
* rule still fires (the SDK does not invoke `.refine()` at registration time). */
|
|
@@ -156,51 +37,6 @@ export const RecordTransactionToolShape = {
|
|
|
156
37
|
network: NetworkSchema,
|
|
157
38
|
confidenceOverride: z.number().min(0).max(1).optional(),
|
|
158
39
|
};
|
|
159
|
-
// ===== synthesize_policy =====
|
|
160
|
-
//
|
|
161
|
-
// Discriminated union on `source` exposes BOTH front-ends through ONE tool.
|
|
162
|
-
// - `source: 'mandate'` -> calls synthesizeFromMandate
|
|
163
|
-
// - `source: 'recording'` -> calls synthesizeFromRecording
|
|
164
|
-
//
|
|
165
|
-
// The MCP SDK's `tool()` API only accepts a flat ZodRawShape (named
|
|
166
|
-
// properties keyed by Zod schemas), so we ALSO export a flat shape used at
|
|
167
|
-
// the transport boundary. The discriminated union is the strict body-side
|
|
168
|
-
// validator; the tool handler runs BOTH and treats the flat shape as the
|
|
169
|
-
// friendly wire contract.
|
|
170
|
-
export const SynthesizePolicyMandateInputSchema = z.object({
|
|
171
|
-
source: z.literal('mandate'),
|
|
172
|
-
mandate: MandateSpecSchema,
|
|
173
|
-
ozConfig: OzAdapterConfigSchema.optional(),
|
|
174
|
-
});
|
|
175
|
-
/** Interpreter opt-in for the recording path. Present -> constraints OZ cannot
|
|
176
|
-
* express (per-method scoping, invocation-count windows, oracle bounds, exact
|
|
177
|
-
* hop paths) lower to a real interpreter predicate document instead of being
|
|
178
|
-
* surfaced as warnings. The core deep-validates `smartAccountAddress` (a C...
|
|
179
|
-
* contract, not the recording's G... source) and the tighten-only oracle
|
|
180
|
-
* bounds; the schema stays light so the core owns the friendly ToolErrors. */
|
|
181
|
-
export const InterpreterOptionsSchema = z.object({
|
|
182
|
-
smartAccountAddress: z.string(),
|
|
183
|
-
installNonce: z.number().int().positive().optional(),
|
|
184
|
-
oracleParams: z
|
|
185
|
-
.object({
|
|
186
|
-
maxStalenessSeconds: z.number().int().positive().optional(),
|
|
187
|
-
maxDeviationBps: z.number().int().positive().optional(),
|
|
188
|
-
})
|
|
189
|
-
.optional(),
|
|
190
|
-
});
|
|
191
|
-
export const SynthesizePolicyRecordingInputSchema = z.object({
|
|
192
|
-
source: z.literal('recording'),
|
|
193
|
-
recordedTx: RecordedTransactionSchema,
|
|
194
|
-
network: NetworkSchema,
|
|
195
|
-
userResponses: ComposeUserResponsesSchema.optional(),
|
|
196
|
-
confidenceOverride: z.object({ threshold: z.number().min(0).max(1) }).optional(),
|
|
197
|
-
interpreter: InterpreterOptionsSchema.optional(),
|
|
198
|
-
ozConfig: OzAdapterConfigSchema.optional(),
|
|
199
|
-
});
|
|
200
|
-
export const SynthesizePolicyInputSchema = z.discriminatedUnion('source', [
|
|
201
|
-
SynthesizePolicyMandateInputSchema,
|
|
202
|
-
SynthesizePolicyRecordingInputSchema,
|
|
203
|
-
]);
|
|
204
40
|
/** Flat ZodRawShape used for MCP tool registration. Every field is optional
|
|
205
41
|
* so the JSON-Schema the SDK exposes to clients does not forbid either
|
|
206
42
|
* front-end; the body re-validates against the discriminated union. */
|
|
@@ -214,26 +50,3 @@ export const SynthesizePolicyToolShape = {
|
|
|
214
50
|
interpreter: InterpreterOptionsSchema.optional(),
|
|
215
51
|
ozConfig: OzAdapterConfigSchema.optional(),
|
|
216
52
|
};
|
|
217
|
-
// ===== Error envelope (canonical) =====
|
|
218
|
-
//
|
|
219
|
-
// Mirrors ToolError from packages/policy-synth/src/errors.ts. We use a
|
|
220
|
-
// `z.string()` for `code` (not an enum) because the core's ErrorCode union
|
|
221
|
-
// evolves over time; the transport contract only promises a string code the
|
|
222
|
-
// caller can dispatch on. A drift test asserts the canonical codes still
|
|
223
|
-
// pass through unchanged.
|
|
224
|
-
export const ToolErrorSchema = z
|
|
225
|
-
.object({
|
|
226
|
-
code: z.string(),
|
|
227
|
-
message: z.string(),
|
|
228
|
-
severity: z.enum(['info', 'warning', 'error', 'fatal']),
|
|
229
|
-
retryable: z.boolean(),
|
|
230
|
-
remediation: z
|
|
231
|
-
.object({
|
|
232
|
-
toolCall: z.object({ name: z.string(), args: z.record(z.unknown()) }).optional(),
|
|
233
|
-
userQuestion: z.object({ code: z.string(), question: z.string() }).optional(),
|
|
234
|
-
docsUrl: z.string().optional(),
|
|
235
|
-
})
|
|
236
|
-
.optional(),
|
|
237
|
-
details: z.unknown().optional(),
|
|
238
|
-
})
|
|
239
|
-
.passthrough();
|
package/dist/src/server.js
CHANGED
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
// registration uses the official MCP SDK's `tool()` API with ZodRawShape
|
|
5
5
|
// schemas (the SDK does not accept ZodEffects / discriminated unions at the
|
|
6
6
|
// tool registration boundary - the known gotcha). The body re-validates
|
|
7
|
-
// against the strict discriminated union in
|
|
8
|
-
// still fail closed.
|
|
7
|
+
// against the strict discriminated union in `@crediolabs/policy-synth/run`
|
|
8
|
+
// so wire inputs still fail closed.
|
|
9
9
|
//
|
|
10
10
|
// Stateless: a fresh McpServer is constructed per transport (stdio/HTTP). No
|
|
11
11
|
// shared mutable state across calls; nothing here caches, queues, or holds
|
|
12
12
|
// key material.
|
|
13
|
+
import { runRecordTransaction, runSynthesizePolicy } from '@crediolabs/policy-synth/run';
|
|
13
14
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
14
15
|
import { RecordTransactionToolShape, SynthesizePolicyToolShape } from "./schemas.js";
|
|
15
16
|
import { mcpResultFromCore } from "./tools/result.js";
|
|
16
|
-
import { runRecordTransaction, runSynthesizePolicy } from "./tools/run.js";
|
|
17
17
|
/** Build a fresh, stateless MCP server. The caller owns the returned object
|
|
18
18
|
* and connects it to a single transport (stdio or Streamable HTTP). */
|
|
19
19
|
export function createMcpServer() {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { type RunRecordTransactionInput, type RunSynthesizePolicyInput, runRecordTransaction, runSynthesizePolicy, } from '@crediolabs/policy-synth/run';
|
|
2
|
+
export { RecordTransactionToolShape, SynthesizePolicyToolShape, } from './schemas.ts';
|
|
3
|
+
export { createMcpServer, registerTools } from './server.ts';
|
|
4
|
+
export type { McpToolError, McpToolResult } from './tools/result.ts';
|
|
5
|
+
export { mcpErrorFromCore, mcpResultFromCore } from './tools/result.ts';
|
|
6
|
+
export { startHttpServer } from './transports/http.ts';
|
|
7
|
+
export { startStdioServer } from './transports/stdio.ts';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// apps/policy-builder-mcp/src/index.ts - public re-exports for the MCP server package.
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.startStdioServer = exports.startHttpServer = exports.mcpResultFromCore = exports.mcpErrorFromCore = exports.registerTools = exports.createMcpServer = exports.SynthesizePolicyToolShape = exports.RecordTransactionToolShape = exports.runSynthesizePolicy = exports.runRecordTransaction = void 0;
|
|
5
|
+
var run_1 = require("@crediolabs/policy-synth/run");
|
|
6
|
+
Object.defineProperty(exports, "runRecordTransaction", { enumerable: true, get: function () { return run_1.runRecordTransaction; } });
|
|
7
|
+
Object.defineProperty(exports, "runSynthesizePolicy", { enumerable: true, get: function () { return run_1.runSynthesizePolicy; } });
|
|
8
|
+
var schemas_ts_1 = require("./schemas.js");
|
|
9
|
+
Object.defineProperty(exports, "RecordTransactionToolShape", { enumerable: true, get: function () { return schemas_ts_1.RecordTransactionToolShape; } });
|
|
10
|
+
Object.defineProperty(exports, "SynthesizePolicyToolShape", { enumerable: true, get: function () { return schemas_ts_1.SynthesizePolicyToolShape; } });
|
|
11
|
+
var server_ts_1 = require("./server.js");
|
|
12
|
+
Object.defineProperty(exports, "createMcpServer", { enumerable: true, get: function () { return server_ts_1.createMcpServer; } });
|
|
13
|
+
Object.defineProperty(exports, "registerTools", { enumerable: true, get: function () { return server_ts_1.registerTools; } });
|
|
14
|
+
var result_ts_1 = require("./tools/result.js");
|
|
15
|
+
Object.defineProperty(exports, "mcpErrorFromCore", { enumerable: true, get: function () { return result_ts_1.mcpErrorFromCore; } });
|
|
16
|
+
Object.defineProperty(exports, "mcpResultFromCore", { enumerable: true, get: function () { return result_ts_1.mcpResultFromCore; } });
|
|
17
|
+
var http_ts_1 = require("./transports/http.js");
|
|
18
|
+
Object.defineProperty(exports, "startHttpServer", { enumerable: true, get: function () { return http_ts_1.startHttpServer; } });
|
|
19
|
+
var stdio_ts_1 = require("./transports/stdio.js");
|
|
20
|
+
Object.defineProperty(exports, "startStdioServer", { enumerable: true, get: function () { return stdio_ts_1.startStdioServer; } });
|