@taplid/mcp 0.4.8 → 0.5.1

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
@@ -12,7 +12,7 @@ Use Taplid from Claude Desktop, Cursor, or any MCP-aware client to verify AI-gen
12
12
 
13
13
  ## What it does
14
14
 
15
- Registers one MCP tool, `taplid_audit`, that accepts three text fields and returns ALLOW / REVIEW / BLOCK with a 0-100 trust score and a structured summary.
15
+ Registers one MCP tool, `taplid_audit`. Only `response` is required; `context`, `prompt`, and `auditMode` are all optional (matches the public API and CLI). Returns ALLOW / REVIEW / BLOCK with a 0-100 trust score and a structured summary.
16
16
 
17
17
  - One tool only. No file reading. No command execution. No repo scanning.
18
18
  - Stdio transport. Local process launched by the MCP client.
@@ -80,15 +80,25 @@ Logs go to stderr only. Stdout is reserved for the MCP protocol.
80
80
 
81
81
  `TAPLID_API_KEY` is read from the MCP client env block.
82
82
 
83
+ ### Input fields
84
+
85
+ | Field | Required | Description |
86
+ |---|---|---|
87
+ | `context` | no | The source material the artifact must be consistent with (diff, spec, code, docs). |
88
+ | `prompt` | no | The prompt or task that produced the artifact. |
89
+ | `response` | yes | The AI-generated artifact to verify. |
90
+ | `auditMode` | no | `'artifact'` (default) or `'standard'`. Artifact mode is recommended for code reviews, PRs, implementation plans, long answers, and structured outputs. Standard mode is best for short factual, policy, refund, pricing, entitlement, and simple answer checks. Still being calibrated. |
91
+
83
92
  ### Worked example
84
93
 
85
- Input:
94
+ Input (short factual check, standard mode):
86
95
 
87
96
  ```json
88
97
  {
89
98
  "context": "The number is 1.",
90
99
  "prompt": "What is the number?",
91
- "response": "The number is 2."
100
+ "response": "The number is 2.",
101
+ "auditMode": "standard"
92
102
  }
93
103
  ```
94
104
 
@@ -97,6 +107,7 @@ Expected envelope:
97
107
  ```json
98
108
  {
99
109
  "auditId": "AUD-XXX...",
110
+ "auditMode": "standard",
100
111
  "decision": "BLOCK",
101
112
  "trustScore": 0,
102
113
  "summary": "This answer conflicts with the provided context.",
@@ -131,6 +142,7 @@ Expected envelope:
131
142
  ### Response fields
132
143
 
133
144
  - **auditId** — unique identifier for this audit run
145
+ - **auditMode** — the effective mode that actually ran: `'artifact'` or `'standard'`
134
146
  - **decision** — `ALLOW`, `REVIEW`, or `BLOCK`
135
147
  - **trustScore** — 0 to 100 public trust signal
136
148
  - **summary** — short explanation for the verdict
@@ -148,7 +160,7 @@ The MCP tool returns structured errors with stable codes; no stack traces, no ra
148
160
 
149
161
  | Code | When |
150
162
  |---|---|
151
- | `INVALID_INPUT` | A required field is missing or not a non-empty string. |
163
+ | `INVALID_INPUT` | `response` is missing or empty, or any other field has an invalid value. |
152
164
  | `INPUT_TOO_LARGE` | One of `context`, `prompt`, `response` exceeds 120,000 characters. Detected pre-network. |
153
165
  | `MISSING_API_KEY` | `TAPLID_API_KEY` is not set. Detected pre-network. |
154
166
  | `UPSTREAM_AUTH_FAILED` | The Taplid API returned 401 or 403. |
@@ -166,6 +178,19 @@ The MCP tool returns structured errors with stable codes; no stack traces, no ra
166
178
  - Logs redact the API key, the `Authorization` header, and the bodies of `context`, `prompt`, `response`. Stdout is reserved for the MCP protocol.
167
179
  - The package never imports core Taplid engine code. The audit pipeline is reached via Taplid's public HTTP audit endpoint, which preserves all server-side guards.
168
180
 
181
+ ## Release notes
182
+
183
+ ### v0.5.0 (ITD-442)
184
+
185
+ Two changes to align the MCP tool with the public API + CLI contract:
186
+
187
+ 1. **Input schema relaxed**: only `response` is required now. `context`, `prompt`, and `auditMode` are all optional. This matches the public Taplid API schema and the CLI. Callers that previously sent all three fields keep working unchanged.
188
+ 2. **`auditMode` is now an exposed field**:
189
+ - `'artifact'` (default when omitted): recommended for code reviews, PRs, implementation plans, long answers, and structured outputs.
190
+ - `'standard'`: best for short factual, policy, refund, pricing, entitlement, and simple answer checks. Still being calibrated.
191
+
192
+ The response envelope now always carries `auditMode` to show the effective mode that actually ran. MCP clients (Claude Desktop, Cursor) that cache tool schemas may need a hard refresh to pick up the new field.
193
+
169
194
  ## License
170
195
 
171
196
  Same license as the parent Taplid workspace.
@@ -1,21 +1,35 @@
1
1
  import { z } from 'zod';
2
- export declare const TAPLID_AUDIT_INPUT_SHAPE: {
3
- readonly context: z.ZodString;
4
- readonly prompt: z.ZodString;
5
- readonly response: z.ZodString;
6
- };
2
+ export declare const TAPLID_AUDIT_INPUT_SHAPE: z.ZodObject<{
3
+ context: z.ZodOptional<z.ZodString>;
4
+ prompt: z.ZodOptional<z.ZodString>;
5
+ response: z.ZodString;
6
+ auditMode: z.ZodOptional<z.ZodEnum<["artifact", "standard"]>>;
7
+ }, "strict", z.ZodTypeAny, {
8
+ response: string;
9
+ context?: string | undefined;
10
+ prompt?: string | undefined;
11
+ auditMode?: "artifact" | "standard" | undefined;
12
+ }, {
13
+ response: string;
14
+ context?: string | undefined;
15
+ prompt?: string | undefined;
16
+ auditMode?: "artifact" | "standard" | undefined;
17
+ }>;
7
18
  export declare const TAPLID_AUDIT_INPUT: z.ZodObject<{
8
- readonly context: z.ZodString;
9
- readonly prompt: z.ZodString;
10
- readonly response: z.ZodString;
19
+ context: z.ZodOptional<z.ZodString>;
20
+ prompt: z.ZodOptional<z.ZodString>;
21
+ response: z.ZodString;
22
+ auditMode: z.ZodOptional<z.ZodEnum<["artifact", "standard"]>>;
11
23
  }, "strict", z.ZodTypeAny, {
12
- context: string;
13
- prompt: string;
14
24
  response: string;
25
+ context?: string | undefined;
26
+ prompt?: string | undefined;
27
+ auditMode?: "artifact" | "standard" | undefined;
15
28
  }, {
16
- context: string;
17
- prompt: string;
18
29
  response: string;
30
+ context?: string | undefined;
31
+ prompt?: string | undefined;
32
+ auditMode?: "artifact" | "standard" | undefined;
19
33
  }>;
20
34
  export type TaplidAuditInput = z.infer<typeof TAPLID_AUDIT_INPUT>;
21
35
  export declare const TOOL_INPUT_JSON_SCHEMA: {
@@ -33,8 +47,13 @@ export declare const TOOL_INPUT_JSON_SCHEMA: {
33
47
  type: "string";
34
48
  description: string;
35
49
  };
50
+ auditMode: {
51
+ type: "string";
52
+ description: string;
53
+ enum: readonly ["artifact", "standard"];
54
+ };
36
55
  };
37
- required: readonly ["context", "prompt", "response"];
56
+ required: readonly ["response"];
38
57
  additionalProperties: false;
39
58
  };
40
59
  export declare const TOOL_DESCRIPTION: string;
@@ -1,28 +1,38 @@
1
1
  import { MAX_REVIEW_FIELD_CHARS } from '@taplid/contract';
2
2
  import { z } from 'zod';
3
- export const TAPLID_AUDIT_INPUT_SHAPE = {
4
- context: z.string().min(1).max(MAX_REVIEW_FIELD_CHARS),
5
- prompt: z.string().min(1).max(MAX_REVIEW_FIELD_CHARS),
3
+ // ITD-442 2026-05-17: only `response` is required. `context`, `prompt`, and
4
+ // `auditMode` are all optional - this matches the public API schema (see
5
+ // `src/api/review-contract.ts` where every field is `.optional()`) and the
6
+ // CLI flag set. The server defaults `auditMode` to 'artifact' when omitted.
7
+ export const TAPLID_AUDIT_INPUT_SHAPE = z.object({
8
+ context: z.string().max(MAX_REVIEW_FIELD_CHARS).optional(),
9
+ prompt: z.string().max(MAX_REVIEW_FIELD_CHARS).optional(),
6
10
  response: z.string().min(1).max(MAX_REVIEW_FIELD_CHARS),
7
- };
8
- export const TAPLID_AUDIT_INPUT = z.object(TAPLID_AUDIT_INPUT_SHAPE).strict();
11
+ auditMode: z.enum(['artifact', 'standard']).optional(),
12
+ }).strict();
13
+ export const TAPLID_AUDIT_INPUT = TAPLID_AUDIT_INPUT_SHAPE;
9
14
  export const TOOL_INPUT_JSON_SCHEMA = {
10
15
  type: 'object',
11
16
  properties: {
12
17
  context: {
13
18
  type: 'string',
14
- description: 'The source material the artifact must be consistent with (diff, spec, code, docs).',
19
+ description: 'Optional. The source material the artifact must be consistent with (diff, spec, code, docs).',
15
20
  },
16
21
  prompt: {
17
22
  type: 'string',
18
- description: 'The prompt or task that produced the artifact.',
23
+ description: 'Optional. The prompt or task that produced the artifact.',
19
24
  },
20
25
  response: {
21
26
  type: 'string',
22
- description: 'The AI-generated artifact to verify.',
27
+ description: 'Required. The AI-generated artifact to verify.',
28
+ },
29
+ auditMode: {
30
+ type: 'string',
31
+ description: "Optional. Audit mode: 'artifact' (default - recommended for code reviews, PRs, implementation plans, long answers, and structured outputs) or 'standard' (best for short factual, policy, refund, pricing, entitlement, and simple answer checks; still being calibrated).",
32
+ enum: ['artifact', 'standard'],
23
33
  },
24
34
  },
25
- required: ['context', 'prompt', 'response'],
35
+ required: ['response'],
26
36
  additionalProperties: false,
27
37
  };
28
38
  export const TOOL_DESCRIPTION = 'Use this before trusting, applying, merging, sending, or acting on an AI-generated artifact. ' +
@@ -1,12 +1,44 @@
1
+ import { PUBLIC_REVIEW_API_RESPONSE_LOCKED_KEY_ORDER, serializePublicReviewResponse, } from '@taplid/contract';
2
+ // Single canonical gate-out for MCP. Routes the upstream API envelope through
3
+ // `serializePublicReviewResponse` (lenient mode) so any zero-filled or
4
+ // invariant-violating diagnostic fields from upstream get stripped before the
5
+ // envelope reaches the AI agent calling the MCP tool. Lenient mode is correct
6
+ // here because MCP is a client surface: it must never throw on a malformed
7
+ // upstream response; it must clean it up and pass it through. Unknown keys
8
+ // (e.g. future server-added fields) survive passthrough because they sit
9
+ // outside the locked key-order array and were not part of the input contract.
1
10
  export function buildMcpOutput(result, cfg) {
2
- const envelope = result.envelope;
11
+ const raw = result.envelope;
12
+ // The canonical serializer enforces required fields (decision/trustScore/
13
+ // summary/issues/nextStep) and emits in locked key order. Any field whose
14
+ // value is undefined/non-finite/invalid is omitted (no zero-fill).
15
+ const canonical = serializePublicReviewResponse(raw, {
16
+ onInvariantViolation: () => {
17
+ // Silent in production. Backend logs the violation at emit time; the
18
+ // MCP client surface must never throw on a malformed upstream response.
19
+ },
20
+ });
21
+ // Preserve any forward-compat keys from `raw` that the contract's locked key
22
+ // order does not know about. The exclude set is the FULL locked-key array
23
+ // (not just the keys the canonical serializer chose to emit) so legacy keys
24
+ // like `appliedPolicy` and `details` - which the canonical intentionally
25
+ // strips per ITD-225 / 2026-05-15 envelope cleanup - cannot be re-injected
26
+ // by the passthrough loop. Truly-unknown future keys (anything not in the
27
+ // locked order) still flow through.
28
+ const lockedKeySet = new Set(PUBLIC_REVIEW_API_RESPONSE_LOCKED_KEY_ORDER);
29
+ const forward = { ...canonical };
30
+ for (const [k, v] of Object.entries(raw)) {
31
+ if (!lockedKeySet.has(k) && v !== undefined) {
32
+ forward[k] = v;
33
+ }
34
+ }
3
35
  if (!cfg.debug)
4
- return envelope;
36
+ return forward;
5
37
  const debug = {
6
38
  latencyMs: result.latencyMs,
7
39
  bytesIn: result.bytesIn,
8
40
  httpStatus: result.httpStatus,
9
41
  };
10
- return { ...envelope, _debug: debug };
42
+ return { ...forward, _debug: debug };
11
43
  }
12
44
  //# sourceMappingURL=mcp-output.js.map
package/dist/server.js CHANGED
@@ -9,7 +9,7 @@ import { handleTaplidAudit } from './tool.js';
9
9
  export function createServer() {
10
10
  const server = new McpServer({
11
11
  name: 'taplid-mcp',
12
- version: '0.4.8',
12
+ version: '0.5.1',
13
13
  });
14
14
  server.registerTool(TOOL_NAME, {
15
15
  description: TOOL_DESCRIPTION,
@@ -1,8 +1,9 @@
1
1
  import type { ResolvedConfig } from './config.js';
2
2
  export interface ReviewRequest {
3
- context: string;
4
- prompt: string;
3
+ context?: string;
4
+ prompt?: string;
5
5
  response: string;
6
+ auditMode?: 'artifact' | 'standard';
6
7
  }
7
8
  export interface ReviewClientDeps {
8
9
  fetchImpl?: typeof globalThis.fetch;
@@ -1,3 +1,4 @@
1
+ import { ContractValidationError, createReviewRequestPayload } from '@taplid/contract';
1
2
  import { McpToolError } from './errors.js';
2
3
  export async function postArtifactReview(req, cfg, deps = {}) {
3
4
  const fetchImpl = deps.fetchImpl ?? globalThis.fetch;
@@ -11,6 +12,25 @@ export async function postArtifactReview(req, cfg, deps = {}) {
11
12
  reject(new McpToolError('UPSTREAM_TIMEOUT', `Taplid API did not respond within ${cfg.timeoutMs}ms. Check TAPLID_API_URL and network connectivity.`));
12
13
  }, cfg.timeoutMs);
13
14
  });
15
+ // Single canonical gate-in for the POST body. createReviewRequestPayload
16
+ // returns the three required keys, trimmed, with hard length caps enforced.
17
+ // auditMode (ITD-442) is appended separately; it is optional and has already
18
+ // been validated by the Zod schema in tool.ts before reaching this function.
19
+ let wireBody;
20
+ try {
21
+ const canonical = createReviewRequestPayload(req);
22
+ const body = { ...canonical };
23
+ if (req.auditMode !== undefined) {
24
+ body['auditMode'] = req.auditMode;
25
+ }
26
+ wireBody = JSON.stringify(body);
27
+ }
28
+ catch (err) {
29
+ if (err instanceof ContractValidationError) {
30
+ throw new McpToolError('INVALID_INPUT', `Taplid request payload is invalid: ${err.message}`);
31
+ }
32
+ throw err;
33
+ }
14
34
  let res;
15
35
  let text;
16
36
  try {
@@ -22,11 +42,7 @@ export async function postArtifactReview(req, cfg, deps = {}) {
22
42
  'content-type': 'application/json',
23
43
  authorization: `Bearer ${cfg.apiKey}`,
24
44
  },
25
- body: JSON.stringify({
26
- context: req.context,
27
- prompt: req.prompt,
28
- response: req.response,
29
- }),
45
+ body: wireBody,
30
46
  signal: controller.signal,
31
47
  }),
32
48
  timeoutSignal,
package/dist/tool.js CHANGED
@@ -17,12 +17,26 @@ export async function handleTaplidAudit(rawInput, deps = {}) {
17
17
  const tooLarge = parsed.error.issues.some((issue) => issue.code === 'too_big');
18
18
  const code = tooLarge ? 'INPUT_TOO_LARGE' : 'INVALID_INPUT';
19
19
  const message = tooLarge
20
- ? 'One of context/prompt/response exceeds the Taplid artifact-mode length limit.'
21
- : 'taplid_audit requires non-empty string fields: context, prompt, response.';
20
+ ? 'One of context/prompt/response exceeds the Taplid length limit.'
21
+ : 'taplid_audit requires a non-empty `response` string; `context`, `prompt`, and `auditMode` are optional.';
22
22
  return toolErrorResult(new McpToolError(code, message));
23
23
  }
24
24
  try {
25
- const result = await postArtifactReview(parsed.data, cfg, deps);
25
+ // ITD-442 2026-05-17: only `response` is required. Build the ReviewRequest
26
+ // object explicitly so exactOptionalPropertyTypes is satisfied - do not
27
+ // spread parsed.data directly. Optional fields are only attached when
28
+ // present so the wire body stays minimal.
29
+ const req = { response: parsed.data.response };
30
+ if (parsed.data.context !== undefined) {
31
+ req.context = parsed.data.context;
32
+ }
33
+ if (parsed.data.prompt !== undefined) {
34
+ req.prompt = parsed.data.prompt;
35
+ }
36
+ if (parsed.data.auditMode !== undefined) {
37
+ req.auditMode = parsed.data.auditMode;
38
+ }
39
+ const result = await postArtifactReview(req, cfg, deps);
26
40
  const envelope = buildMcpOutput(result, cfg);
27
41
  const decision = typeof envelope.decision === 'string'
28
42
  ? envelope.decision
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taplid/mcp",
3
- "version": "0.4.8",
3
+ "version": "0.5.1",
4
4
  "description": "Local MCP (Model Context Protocol) server exposing Taplid artifact audit as a single tool for AI coding agents.",
5
5
  "keywords": [
6
6
  "taplid",
@@ -36,7 +36,7 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "@modelcontextprotocol/sdk": "^1.29.0",
39
- "@taplid/contract": "0.4.8",
39
+ "@taplid/contract": "0.5.1",
40
40
  "pino": "^9.0.0",
41
41
  "zod": "^3.0.0"
42
42
  },