@treza/mcp 0.1.0 → 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/README.md CHANGED
@@ -38,6 +38,8 @@ treza-mcp
38
38
  |---|---|---|
39
39
  | `TREZA_BASE_URL` | `https://app.trezalabs.com` | Treza Platform API URL |
40
40
  | `TREZA_TIMEOUT` | `30000` | Request timeout in milliseconds |
41
+ | `TREZA_API_KEY` | — | API key (`treza_live_...`) for authenticated tools like `treza_redact_text` and `treza_pii_audit_query` |
42
+ | `TREZA_ACCOUNT` | — | Optional tenant account the API key belongs to |
41
43
 
42
44
  ## Available Tools
43
45
 
@@ -72,6 +74,16 @@ treza-mcp
72
74
  | `treza_list_api_keys` | List scoped API keys |
73
75
  | `treza_create_api_key` | Create a new API key with specific permissions |
74
76
 
77
+ ### PII & Redaction
78
+
79
+ | Tool | Description |
80
+ |---|---|
81
+ | `treza_redact_text` | Redact PII from a text string via the redaction enclave; returns redacted text, detected entities, and mode (requires `TREZA_API_KEY` with `redact:run`) |
82
+ | `treza_redact_chat_completions` | OpenAI-compatible chat completion with PII redacted before forwarding upstream; returns the provider response, request id, mode, and optional rehydration map (requires `TREZA_API_KEY` with `redact:proxy`) |
83
+ | `treza_pii_scan` | Heuristically classify which fields in a JSON payload contain PII |
84
+ | `treza_pii_workflow_check` | Audit a workflow definition for where PII flows in, is stripped, or leaks |
85
+ | `treza_pii_audit_query` | Query the PII audit log for past workflow runs (requires `TREZA_API_KEY`) |
86
+
75
87
  ## MCP Resources
76
88
 
77
89
  The server also exposes browsable resources that give agents ambient context:
package/dist/handlers.js CHANGED
@@ -2,6 +2,22 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleToolCall = handleToolCall;
4
4
  const tools_1 = require("./tools");
5
+ // Built-in PII field name heuristics (case-insensitive substring match)
6
+ const PII_HEURISTICS = [
7
+ 'name', 'firstname', 'lastname', 'fullname',
8
+ 'email', 'phone', 'mobile', 'tel',
9
+ 'dob', 'dateofbirth', 'birthdate', 'birthday', 'age',
10
+ 'ssn', 'sin', 'nin', 'taxid', 'passport', 'license',
11
+ 'address', 'street', 'city', 'zip', 'postcode', 'country',
12
+ 'ip', 'deviceid', 'userid', 'nationalid', 'gender',
13
+ 'salary', 'income', 'creditcard', 'cardnumber', 'iban',
14
+ 'biometric', 'fingerprint', 'faceid',
15
+ ];
16
+ function isPIIField(fieldName, customFields) {
17
+ const lower = fieldName.toLowerCase().replace(/[_\-\s]/g, '');
18
+ return (PII_HEURISTICS.some(h => lower.includes(h)) ||
19
+ customFields.some(f => lower.includes(f.toLowerCase().replace(/[_\-\s]/g, ''))));
20
+ }
5
21
  function ok(data) {
6
22
  return {
7
23
  content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
@@ -154,6 +170,92 @@ async function handleToolCall(client, toolName, args) {
154
170
  apiKey,
155
171
  });
156
172
  }
173
+ // ── PII Redaction ───────────────────────────────────────────────
174
+ case 'treza_redact_text': {
175
+ const { text } = tools_1.redactTextSchema.parse(args);
176
+ const result = await client.redactText(text);
177
+ return ok(result);
178
+ }
179
+ case 'treza_redact_chat_completions': {
180
+ const { messages, model, modelKey, proxyId, rehydrate } = tools_1.redactChatCompletionsSchema.parse(args);
181
+ const result = await client.redactChatCompletions({ model, messages }, {
182
+ ...(modelKey && { modelKey }),
183
+ ...(proxyId && { proxyId }),
184
+ ...(rehydrate && { rehydrate }),
185
+ });
186
+ return ok(result);
187
+ }
188
+ // ── PII Tracking ────────────────────────────────────────────────
189
+ case 'treza_pii_scan': {
190
+ const { payload, customPIIFields = [] } = tools_1.piiScanSchema.parse(args);
191
+ const fields = Object.keys(payload);
192
+ const piiFields = fields.filter(f => isPIIField(f, customPIIFields));
193
+ const nonPIIFields = fields.filter(f => !isPIIField(f, customPIIFields));
194
+ const riskLevel = piiFields.length === 0 ? 'none' : piiFields.length <= 2 ? 'low' : piiFields.length <= 5 ? 'medium' : 'high';
195
+ return ok({
196
+ summary: {
197
+ totalFields: fields.length,
198
+ piiFieldCount: piiFields.length,
199
+ riskLevel,
200
+ },
201
+ piiFields: piiFields.map(f => ({ field: f, classification: 'pii' })),
202
+ nonPIIFields: nonPIIFields.map(f => ({ field: f, classification: 'non-pii' })),
203
+ recommendation: piiFields.length > 0
204
+ ? `This payload contains ${piiFields.length} PII field(s): ${piiFields.join(', ')}. Use PIIContext.fromObject() and declare requiresPII on each WorkflowStep to control where these fields flow.`
205
+ : 'No PII detected. Safe to pass to any workflow step.',
206
+ });
207
+ }
208
+ case 'treza_pii_workflow_check': {
209
+ const { workflow, initialPayload = {} } = tools_1.piiWorkflowCheckSchema.parse(args);
210
+ const payloadFields = Object.keys(initialPayload);
211
+ const detectedPIIFields = payloadFields.filter(f => isPIIField(f, []));
212
+ const stepAnalysis = workflow.steps.map((step, index) => {
213
+ const prevStepAllowed = index === 0
214
+ ? detectedPIIFields
215
+ : workflow.steps.slice(0, index).reduce((acc, s) => {
216
+ return acc.filter(f => s.requiresPII.includes(f));
217
+ }, detectedPIIFields);
218
+ const piiThatWouldArrive = prevStepAllowed;
219
+ const piiStripped = piiThatWouldArrive.filter(f => !step.requiresPII.includes(f));
220
+ const violation = piiThatWouldArrive.length > 0 && step.requiresPII.length === 0;
221
+ return {
222
+ stepId: step.id,
223
+ stepName: step.name,
224
+ requiresPII: step.requiresPII,
225
+ piiFieldsReceived: piiThatWouldArrive.filter(f => step.requiresPII.includes(f)),
226
+ piiFieldsStripped: piiStripped,
227
+ violation,
228
+ violationNote: violation
229
+ ? `PII fields [${piiThatWouldArrive.join(', ')}] are present in context but this step declares requiresPII: []. They will be stripped — verify this is intentional.`
230
+ : null,
231
+ };
232
+ });
233
+ const violations = stepAnalysis.filter(s => s.violation);
234
+ return ok({
235
+ workflowId: workflow.id,
236
+ workflowName: workflow.name,
237
+ summary: {
238
+ totalSteps: workflow.steps.length,
239
+ stepsReceivingPII: stepAnalysis.filter(s => s.piiFieldsReceived.length > 0).length,
240
+ violations: violations.length,
241
+ status: violations.length === 0 ? 'clean' : 'violations_detected',
242
+ },
243
+ steps: stepAnalysis,
244
+ ...(violations.length > 0 && {
245
+ actionRequired: `${violations.length} step(s) will silently strip PII. Review these steps to confirm requiresPII is correctly declared.`,
246
+ }),
247
+ });
248
+ }
249
+ case 'treza_pii_audit_query': {
250
+ const { workflowId, walletAddress, violationsOnly, limit = 50 } = tools_1.piiAuditQuerySchema.parse(args);
251
+ const data = await client.queryPIIAudit({
252
+ workflowId: workflowId ?? undefined,
253
+ walletAddress: walletAddress ?? undefined,
254
+ violations: violationsOnly ?? false,
255
+ limit: Math.min(limit, 500),
256
+ });
257
+ return ok(data);
258
+ }
157
259
  default:
158
260
  return err(`Unknown tool: ${toolName}`);
159
261
  }
package/dist/index.js CHANGED
@@ -9,13 +9,18 @@ const handlers_1 = require("./handlers");
9
9
  const resources_1 = require("./resources");
10
10
  const TREZA_BASE_URL = process.env.TREZA_BASE_URL || 'https://app.trezalabs.com';
11
11
  const TREZA_TIMEOUT = parseInt(process.env.TREZA_TIMEOUT || '30000', 10);
12
+ // Authenticated endpoints (e.g. /api/pii/audit) need an API key + account
13
+ const TREZA_API_KEY = process.env.TREZA_API_KEY;
14
+ const TREZA_ACCOUNT = process.env.TREZA_ACCOUNT || process.env.TREZA_WALLET;
12
15
  const client = new treza_client_1.TrezaClient({
13
16
  baseUrl: TREZA_BASE_URL,
14
17
  timeout: TREZA_TIMEOUT,
18
+ apiKey: TREZA_API_KEY,
19
+ account: TREZA_ACCOUNT,
15
20
  });
16
21
  const server = new mcp_js_1.McpServer({
17
22
  name: 'treza-enclaves',
18
- version: '0.1.0',
23
+ version: '0.2.0',
19
24
  });
20
25
  // ─── Register Tools ─────────────────────────────────────────────────────────
21
26
  for (const tool of tools_1.TOOL_DEFINITIONS) {
package/dist/resources.js CHANGED
@@ -27,6 +27,12 @@ exports.RESOURCE_TEMPLATES = [
27
27
  description: 'Quick verification status and trust level for an enclave',
28
28
  mimeType: 'application/json',
29
29
  },
30
+ {
31
+ uriTemplate: 'treza://pii/audit/{workflowId}',
32
+ name: 'PII Audit Log',
33
+ description: 'PII access events for a specific workflow, showing which fields were present, allowed, and stripped at each step boundary. Violations are flagged where PII flowed into a step that declared requiresPII: [].',
34
+ mimeType: 'application/json',
35
+ },
30
36
  ];
31
37
  async function handleResourceRead(client, uri) {
32
38
  const url = new URL(uri);
@@ -55,5 +61,11 @@ async function handleResourceRead(client, uri) {
55
61
  const status = await client.getVerificationStatus(enclaveId);
56
62
  return JSON.stringify(status, null, 2);
57
63
  }
64
+ // treza://pii/audit/{workflowId}
65
+ if (url.host === 'pii' && pathParts.length === 2 && pathParts[0] === 'audit') {
66
+ const workflowId = pathParts[1];
67
+ const data = await client.queryPIIAudit({ workflowId });
68
+ return JSON.stringify(data, null, 2);
69
+ }
58
70
  throw new Error(`Unknown resource URI: ${uri}`);
59
71
  }
package/dist/tools.d.ts CHANGED
@@ -187,6 +187,131 @@ export declare const createApiKeySchema: z.ZodObject<{
187
187
  name: string;
188
188
  permissions: ("enclaves:read" | "enclaves:write" | "tasks:read" | "tasks:write" | "logs:read")[];
189
189
  }>;
190
+ export declare const piiScanSchema: z.ZodObject<{
191
+ payload: z.ZodRecord<z.ZodString, z.ZodUnknown>;
192
+ customPIIFields: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
193
+ }, "strip", z.ZodTypeAny, {
194
+ payload: Record<string, unknown>;
195
+ customPIIFields?: string[] | undefined;
196
+ }, {
197
+ payload: Record<string, unknown>;
198
+ customPIIFields?: string[] | undefined;
199
+ }>;
200
+ export declare const piiWorkflowCheckSchema: z.ZodObject<{
201
+ workflow: z.ZodObject<{
202
+ id: z.ZodString;
203
+ name: z.ZodString;
204
+ steps: z.ZodArray<z.ZodObject<{
205
+ id: z.ZodString;
206
+ name: z.ZodString;
207
+ requiresPII: z.ZodArray<z.ZodString, "many">;
208
+ }, "strip", z.ZodTypeAny, {
209
+ name: string;
210
+ id: string;
211
+ requiresPII: string[];
212
+ }, {
213
+ name: string;
214
+ id: string;
215
+ requiresPII: string[];
216
+ }>, "many">;
217
+ }, "strip", z.ZodTypeAny, {
218
+ name: string;
219
+ id: string;
220
+ steps: {
221
+ name: string;
222
+ id: string;
223
+ requiresPII: string[];
224
+ }[];
225
+ }, {
226
+ name: string;
227
+ id: string;
228
+ steps: {
229
+ name: string;
230
+ id: string;
231
+ requiresPII: string[];
232
+ }[];
233
+ }>;
234
+ initialPayload: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
235
+ }, "strip", z.ZodTypeAny, {
236
+ workflow: {
237
+ name: string;
238
+ id: string;
239
+ steps: {
240
+ name: string;
241
+ id: string;
242
+ requiresPII: string[];
243
+ }[];
244
+ };
245
+ initialPayload?: Record<string, unknown> | undefined;
246
+ }, {
247
+ workflow: {
248
+ name: string;
249
+ id: string;
250
+ steps: {
251
+ name: string;
252
+ id: string;
253
+ requiresPII: string[];
254
+ }[];
255
+ };
256
+ initialPayload?: Record<string, unknown> | undefined;
257
+ }>;
258
+ export declare const redactTextSchema: z.ZodObject<{
259
+ text: z.ZodString;
260
+ }, "strip", z.ZodTypeAny, {
261
+ text: string;
262
+ }, {
263
+ text: string;
264
+ }>;
265
+ export declare const redactChatCompletionsSchema: z.ZodObject<{
266
+ messages: z.ZodArray<z.ZodObject<{
267
+ role: z.ZodString;
268
+ content: z.ZodString;
269
+ }, "strip", z.ZodTypeAny, {
270
+ role: string;
271
+ content: string;
272
+ }, {
273
+ role: string;
274
+ content: string;
275
+ }>, "many">;
276
+ model: z.ZodDefault<z.ZodString>;
277
+ modelKey: z.ZodOptional<z.ZodString>;
278
+ proxyId: z.ZodOptional<z.ZodString>;
279
+ rehydrate: z.ZodOptional<z.ZodBoolean>;
280
+ }, "strip", z.ZodTypeAny, {
281
+ messages: {
282
+ role: string;
283
+ content: string;
284
+ }[];
285
+ model: string;
286
+ modelKey?: string | undefined;
287
+ proxyId?: string | undefined;
288
+ rehydrate?: boolean | undefined;
289
+ }, {
290
+ messages: {
291
+ role: string;
292
+ content: string;
293
+ }[];
294
+ model?: string | undefined;
295
+ modelKey?: string | undefined;
296
+ proxyId?: string | undefined;
297
+ rehydrate?: boolean | undefined;
298
+ }>;
299
+ export declare const piiAuditQuerySchema: z.ZodObject<{
300
+ workflowId: z.ZodOptional<z.ZodString>;
301
+ walletAddress: z.ZodOptional<z.ZodString>;
302
+ violationsOnly: z.ZodOptional<z.ZodBoolean>;
303
+ limit: z.ZodOptional<z.ZodNumber>;
304
+ }, "strip", z.ZodTypeAny, {
305
+ walletAddress?: string | undefined;
306
+ limit?: number | undefined;
307
+ workflowId?: string | undefined;
308
+ violationsOnly?: boolean | undefined;
309
+ }, {
310
+ walletAddress?: string | undefined;
311
+ limit?: number | undefined;
312
+ workflowId?: string | undefined;
313
+ violationsOnly?: boolean | undefined;
314
+ }>;
190
315
  export interface ToolDefinition {
191
316
  name: string;
192
317
  description: string;
package/dist/tools.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TOOL_DEFINITIONS = exports.createApiKeySchema = exports.listApiKeysSchema = exports.createTaskSchema = exports.listTasksSchema = exports.getProviderSchema = exports.listProvidersSchema = exports.getVerificationStatusSchema = exports.verifyAttestationSchema = exports.getAttestationSchema = exports.getEnclaveLogsSchema = exports.enclaveActionSchema = exports.deleteEnclaveSchema = exports.updateEnclaveSchema = exports.createEnclaveSchema = exports.getEnclaveSchema = exports.listEnclavesSchema = void 0;
3
+ exports.TOOL_DEFINITIONS = exports.piiAuditQuerySchema = exports.redactChatCompletionsSchema = exports.redactTextSchema = exports.piiWorkflowCheckSchema = exports.piiScanSchema = exports.createApiKeySchema = exports.listApiKeysSchema = exports.createTaskSchema = exports.listTasksSchema = exports.getProviderSchema = exports.listProvidersSchema = exports.getVerificationStatusSchema = exports.verifyAttestationSchema = exports.getAttestationSchema = exports.getEnclaveLogsSchema = exports.enclaveActionSchema = exports.deleteEnclaveSchema = exports.updateEnclaveSchema = exports.createEnclaveSchema = exports.getEnclaveSchema = exports.listEnclavesSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  /**
6
6
  * Zod schemas and metadata for all MCP tools exposed by the Treza server.
@@ -83,6 +83,42 @@ exports.createApiKeySchema = zod_1.z.object({
83
83
  permissions: zod_1.z.array(zod_1.z.enum(['enclaves:read', 'enclaves:write', 'tasks:read', 'tasks:write', 'logs:read'])).describe('Array of permission scopes'),
84
84
  walletAddress: zod_1.z.string().describe('Wallet address for authorization'),
85
85
  });
86
+ // ─── PII Tracking ────────────────────────────────────────────────────────────
87
+ exports.piiScanSchema = zod_1.z.object({
88
+ payload: zod_1.z.record(zod_1.z.unknown()).describe('The JSON object (agent context or step input) to scan for PII fields'),
89
+ customPIIFields: zod_1.z.array(zod_1.z.string()).optional().describe('Additional field names to treat as PII beyond the built-in heuristics (e.g. ["employeeId", "taxId"])'),
90
+ });
91
+ exports.piiWorkflowCheckSchema = zod_1.z.object({
92
+ workflow: zod_1.z.object({
93
+ id: zod_1.z.string().describe('Workflow identifier'),
94
+ name: zod_1.z.string().describe('Human-readable workflow name'),
95
+ steps: zod_1.z.array(zod_1.z.object({
96
+ id: zod_1.z.string(),
97
+ name: zod_1.z.string(),
98
+ requiresPII: zod_1.z.array(zod_1.z.string()).describe('PII field names this step is allowed to receive'),
99
+ })).describe('Ordered list of workflow steps with their PII declarations'),
100
+ }).describe('Workflow definition to analyse'),
101
+ initialPayload: zod_1.z.record(zod_1.z.unknown()).optional().describe('The data object that will be passed into the first step, used to show which fields are affected'),
102
+ });
103
+ exports.redactTextSchema = zod_1.z.object({
104
+ text: zod_1.z.string().describe('The text to redact. PII is detected and replaced with typed placeholders (e.g. [EMAIL_1]).'),
105
+ });
106
+ exports.redactChatCompletionsSchema = zod_1.z.object({
107
+ messages: zod_1.z.array(zod_1.z.object({
108
+ role: zod_1.z.string().describe('Message role (e.g. "system", "user", "assistant")'),
109
+ content: zod_1.z.string().describe('Message content; PII is redacted before forwarding upstream'),
110
+ })).describe('OpenAI-style chat messages'),
111
+ model: zod_1.z.string().default('gpt-4o-mini').describe('Upstream model name'),
112
+ modelKey: zod_1.z.string().optional().describe('Upstream provider API key (x-model-key). Omit when the selected proxy stores one.'),
113
+ proxyId: zod_1.z.string().optional().describe('Configured redaction proxy id (x-treza-proxy) carrying the upstream URL, stored key, and policy'),
114
+ rehydrate: zod_1.z.boolean().optional().describe('When true, also return the placeholder → original map so values can be restored in the response'),
115
+ });
116
+ exports.piiAuditQuerySchema = zod_1.z.object({
117
+ workflowId: zod_1.z.string().optional().describe('Filter events by workflow ID'),
118
+ walletAddress: zod_1.z.string().optional().describe('Filter events by wallet address'),
119
+ violationsOnly: zod_1.z.boolean().optional().describe('If true, return only events where PII flowed into a step that declared requiresPII: []'),
120
+ limit: zod_1.z.number().optional().describe('Maximum number of events to return (default 50, max 500)'),
121
+ });
86
122
  exports.TOOL_DEFINITIONS = [
87
123
  {
88
124
  name: 'treza_list_enclaves',
@@ -164,4 +200,29 @@ exports.TOOL_DEFINITIONS = [
164
200
  description: 'Create a new scoped API key for programmatic access to the Treza platform. Returns the key only once — store it securely.',
165
201
  schema: exports.createApiKeySchema,
166
202
  },
203
+ {
204
+ name: 'treza_pii_scan',
205
+ description: 'Scan a JSON payload for fields that likely contain Personally Identifiable Information (PII). Uses built-in heuristics (name, email, dob, ssn, address, passport, etc.) plus any custom fields you supply. Returns a classified field list and a risk summary. Use this before passing context to a workflow step to understand what sensitive data is present.',
206
+ schema: exports.piiScanSchema,
207
+ },
208
+ {
209
+ name: 'treza_pii_workflow_check',
210
+ description: 'Analyse a full workflow definition and report which steps receive PII, which strip it, and flag any steps where PII would flow in despite the step declaring requiresPII: []. Use this to audit a workflow design before running it, or to document your data-flow for compliance purposes.',
211
+ schema: exports.piiWorkflowCheckSchema,
212
+ },
213
+ {
214
+ name: 'treza_redact_text',
215
+ description: 'Redact PII from a text string using the Treza redaction enclave. Returns the redacted text (PII replaced with typed placeholders like [EMAIL_1]), the detected entities, and the redaction mode (tee/standard). The original text is never stored or forwarded. Use this before sending agent context to an external LLM or storing it. Requires TREZA_API_KEY with the redact:run permission.',
216
+ schema: exports.redactTextSchema,
217
+ },
218
+ {
219
+ name: 'treza_redact_chat_completions',
220
+ description: 'OpenAI-compatible chat completion with PII redacted before the prompt is forwarded to the upstream model provider. Message content is stripped of PII, sent upstream, and the provider response is returned verbatim along with the Treza request id and mode. Pass rehydrate:true to also get the placeholder → original map so you can restore values in the response. Provide either modelKey (upstream key) or proxyId (a configured proxy that stores one). Requires TREZA_API_KEY with the redact:proxy permission.',
221
+ schema: exports.redactChatCompletionsSchema,
222
+ },
223
+ {
224
+ name: 'treza_pii_audit_query',
225
+ description: 'Query the PII audit log for past workflow runs. Returns PIIAccessEvent records showing which PII fields were present, allowed, and stripped at each step boundary. Filter by workflowId, walletAddress, or show only violation events where PII leaked into a step that should not have received it.',
226
+ schema: exports.piiAuditQuerySchema,
227
+ },
167
228
  ];
@@ -1,6 +1,10 @@
1
1
  export interface TrezaConfig {
2
2
  baseUrl?: string;
3
3
  timeout?: number;
4
+ /** Treza API key (treza_live_... / treza_test_...) — required for authenticated endpoints like /api/pii/audit. */
5
+ apiKey?: string;
6
+ /** Tenant account the API key belongs to; sent as the x-treza-account header. */
7
+ account?: string;
4
8
  }
5
9
  export declare class TrezaSdkError extends Error {
6
10
  readonly code?: string;
@@ -34,4 +38,33 @@ export declare class TrezaClient {
34
38
  createTask(request: Record<string, unknown>): Promise<any>;
35
39
  getApiKeys(walletAddress: string): Promise<any>;
36
40
  createApiKey(request: Record<string, unknown>): Promise<any>;
41
+ /**
42
+ * Redact PII from a text string via the redaction enclave (`POST /api/redact/run`).
43
+ * Requires an API key with the `redact:run` permission. The original text is
44
+ * never stored or forwarded.
45
+ */
46
+ redactText(text: string): Promise<any>;
47
+ /**
48
+ * OpenAI-compatible chat completions with PII redacted before forwarding
49
+ * upstream (`POST /api/redact/chat/completions`). Requires an API key with the
50
+ * `redact:proxy` permission and either a stored proxy key (via `proxyId`) or an
51
+ * upstream `modelKey`. Streaming is not supported here. Returns the upstream
52
+ * response body plus the Treza request id, mode, and (optional) rehydration map.
53
+ */
54
+ redactChatCompletions(request: Record<string, unknown>, options?: {
55
+ modelKey?: string;
56
+ proxyId?: string;
57
+ rehydrate?: boolean;
58
+ }): Promise<{
59
+ rehydration?: Record<string, string> | undefined;
60
+ data: any;
61
+ requestId: string;
62
+ mode: string;
63
+ }>;
64
+ queryPIIAudit(params: {
65
+ workflowId?: string;
66
+ walletAddress?: string;
67
+ violations?: boolean;
68
+ limit?: number;
69
+ }): Promise<any>;
37
70
  }
@@ -20,10 +20,15 @@ exports.TrezaSdkError = TrezaSdkError;
20
20
  */
21
21
  class TrezaClient {
22
22
  constructor(config = {}) {
23
+ const headers = { 'Content-Type': 'application/json' };
24
+ if (config.apiKey)
25
+ headers['Authorization'] = `Bearer ${config.apiKey}`;
26
+ if (config.account)
27
+ headers['x-treza-account'] = config.account;
23
28
  this.client = axios_1.default.create({
24
29
  baseURL: config.baseUrl || 'https://app.trezalabs.com',
25
30
  timeout: config.timeout || 30000,
26
- headers: { 'Content-Type': 'application/json' },
31
+ headers,
27
32
  });
28
33
  this.client.interceptors.response.use((r) => r, (error) => {
29
34
  if (error.response) {
@@ -109,5 +114,58 @@ class TrezaClient {
109
114
  const r = await this.client.post('/api/api-keys', request);
110
115
  return r.data.apiKey;
111
116
  }
117
+ // ── PII Redaction ──────────────────────────────────────────────────────────
118
+ /**
119
+ * Redact PII from a text string via the redaction enclave (`POST /api/redact/run`).
120
+ * Requires an API key with the `redact:run` permission. The original text is
121
+ * never stored or forwarded.
122
+ */
123
+ async redactText(text) {
124
+ const r = await this.client.post('/api/redact/run', { text });
125
+ return r.data;
126
+ }
127
+ /**
128
+ * OpenAI-compatible chat completions with PII redacted before forwarding
129
+ * upstream (`POST /api/redact/chat/completions`). Requires an API key with the
130
+ * `redact:proxy` permission and either a stored proxy key (via `proxyId`) or an
131
+ * upstream `modelKey`. Streaming is not supported here. Returns the upstream
132
+ * response body plus the Treza request id, mode, and (optional) rehydration map.
133
+ */
134
+ async redactChatCompletions(request, options = {}) {
135
+ const headers = {};
136
+ if (options.modelKey)
137
+ headers['x-model-key'] = options.modelKey;
138
+ if (options.proxyId)
139
+ headers['x-treza-proxy'] = options.proxyId;
140
+ if (options.rehydrate)
141
+ headers['x-treza-rehydrate'] = '1';
142
+ const r = await this.client.post('/api/redact/chat/completions', { ...request, stream: false }, { headers });
143
+ let rehydration;
144
+ const rehydrationHeader = r.headers['x-treza-rehydration'];
145
+ if (typeof rehydrationHeader === 'string' && rehydrationHeader) {
146
+ try {
147
+ rehydration = JSON.parse(rehydrationHeader);
148
+ }
149
+ catch {
150
+ rehydration = undefined;
151
+ }
152
+ }
153
+ return {
154
+ data: r.data,
155
+ requestId: r.headers['x-treza-request-id'] || '',
156
+ mode: r.headers['x-treza-mode'] || 'standard',
157
+ ...(rehydration ? { rehydration } : {}),
158
+ };
159
+ }
160
+ // ── PII Audit ────────────────────────────────────────────────────────────
161
+ async queryPIIAudit(params) {
162
+ const r = await this.client.get('/api/pii/audit', { params: {
163
+ ...(params.workflowId && { workflowId: params.workflowId }),
164
+ ...(params.walletAddress && { walletAddress: params.walletAddress }),
165
+ ...(params.violations && { violations: 'true' }),
166
+ limit: params.limit ?? 50,
167
+ } });
168
+ return r.data;
169
+ }
112
170
  }
113
171
  exports.TrezaClient = TrezaClient;
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@treza/mcp",
3
- "version": "0.1.0",
4
- "description": "Model Context Protocol server for Treza Enclaves — lets AI agents manage TEEs, verify attestations, and sign transactions",
3
+ "version": "0.2.0",
4
+ "mcpName": "io.github.treza-labs/treza",
5
+ "description": "Model Context Protocol server for Treza Enclaves — lets AI agents manage TEEs, verify attestations, redact PII, and sign transactions",
5
6
  "keywords": [
6
7
  "treza",
7
8
  "mcp",
@@ -10,7 +11,9 @@
10
11
  "enclaves",
11
12
  "tee",
12
13
  "attestation",
13
- "nitro-enclaves"
14
+ "nitro-enclaves",
15
+ "pii",
16
+ "redaction"
14
17
  ],
15
18
  "homepage": "https://trezalabs.com",
16
19
  "repository": {