New:Socket for Asana Is Now Available.Learn more
Sign In

@trigguard/mcp-server

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@trigguard/mcp-server - npm Package Compare versions

Comparing version
1.1.0
to
1.2.0
+28
dist/errors.d.ts
import { toolError } from "./mcpResponse.js";
export type FailureClass = "TRIGGUARD_APPLICATION_ERROR" | "EDGE_HTML_ERROR" | "UPSTREAM_PROXY_ERROR" | "NETWORK_ERROR" | "AUTHENTICATION_ERROR" | "ORGANIZATION_BINDING_ERROR";
export type GatewayFailureCapture = {
status?: number;
bodyText?: string;
contentType?: string;
};
export type McpCodedError = Error & {
readonly code?: string;
readonly status?: number;
readonly remediation?: string;
readonly bodyText?: string;
readonly contentType?: string;
};
export declare function sanitizePublicMessage(message: string): string;
export declare function codedError(code: string, message: string, remediation?: string, status?: number): McpCodedError;
export declare function looksLikeHtml(text: string, contentType?: string): boolean;
export declare function parseJsonObject(text: string): Record<string, unknown> | null;
export declare function looksLikeTrigGuardJson(text: string): boolean;
export declare function enrichFailure(err: unknown, capture: GatewayFailureCapture | null): unknown;
export declare function wrapFetchWithCapture(fetchImpl?: typeof fetch): {
fetchImpl: typeof fetch;
capture: {
last: GatewayFailureCapture | null;
};
};
export declare function mapToolFailure(err: unknown): ReturnType<typeof toolError>;
//# sourceMappingURL=errors.d.ts.map
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAI7C,MAAM,MAAM,YAAY,GACpB,6BAA6B,GAC7B,iBAAiB,GACjB,sBAAsB,GACtB,eAAe,GACf,sBAAsB,GACtB,4BAA4B,CAAC;AAEjC,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG;IAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,MAAM,GACd,aAAa,CAEf;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CASzE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAU5E;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAS5D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,GAAG,IAAI,GAAG,OAAO,CAS1F;AAED,wBAAgB,oBAAoB,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,GAAG;IAC9D,SAAS,EAAE,OAAO,KAAK,CAAC;IACxB,OAAO,EAAE;QAAE,IAAI,EAAE,qBAAqB,GAAG,IAAI,CAAA;KAAE,CAAC;CACjD,CAmCA;AAWD,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CA6JzE"}
import { toolError } from "./mcpResponse.js";
const SECRET_RE = /tg_live_[A-Za-z0-9_-]+|Bearer\s+\S+|sk-[A-Za-z0-9]+/gi;
export function sanitizePublicMessage(message) {
return message.replace(SECRET_RE, "[redacted]").replace(/\s+/g, " ").trim().slice(0, 280);
}
export function codedError(code, message, remediation, status) {
return Object.assign(new Error(message), { code, remediation, status });
}
export function looksLikeHtml(text, contentType) {
const ct = (contentType ?? "").toLowerCase();
if (ct.includes("text/html"))
return true;
const head = text.trim().slice(0, 400).toLowerCase();
return (head.startsWith("<!doctype html") ||
head.startsWith("<html") ||
(head.includes("<html") && (head.includes("cloudflare") || head.includes("attention required"))));
}
export function parseJsonObject(text) {
try {
const value = JSON.parse(text);
if (value && typeof value === "object" && !Array.isArray(value)) {
return value;
}
return null;
}
catch {
return null;
}
}
export function looksLikeTrigGuardJson(text) {
const obj = parseJsonObject(text);
if (!obj)
return false;
if (typeof obj.protocol === "string" && obj.protocol.toLowerCase().includes("trigguard"))
return true;
if (typeof obj.authority === "string" && obj.authority.toLowerCase().includes("trigguard"))
return true;
if (typeof obj.error === "string" && String(obj.error).startsWith("TG_"))
return true;
if (typeof obj.code === "string" && String(obj.code).startsWith("TG_"))
return true;
const decision = typeof obj.decision === "string" ? obj.decision.toUpperCase() : "";
return decision === "PERMIT" || decision === "DENY" || decision === "SILENCE" || decision === "ESCALATE";
}
export function enrichFailure(err, capture) {
if (!capture)
return err;
const base = err instanceof Error ? err : new Error(String(err));
const current = base;
return Object.assign(base, {
status: current.status ?? capture.status,
bodyText: current.bodyText ?? capture.bodyText,
contentType: current.contentType ?? capture.contentType,
});
}
export function wrapFetchWithCapture(fetchImpl) {
const capture = { last: null };
const impl = fetchImpl ?? fetch;
const wrapped = (async (input, init) => {
try {
const res = await impl(input, init);
const contentType = typeof res.headers?.get === "function" ? (res.headers.get("content-type") ?? "") : "";
const bodyText = await res.text();
if (!res.ok) {
capture.last = { status: res.status, bodyText, contentType };
}
return {
ok: res.ok,
status: res.status,
headers: res.headers,
text: async () => bodyText,
json: async () => {
try {
return JSON.parse(bodyText);
}
catch {
return {};
}
},
};
}
catch (e) {
capture.last = {
status: 0,
bodyText: e instanceof Error ? e.message : String(e),
contentType: "",
};
throw e;
}
});
return { fetchImpl: wrapped, capture };
}
function classifiedToolError(code, message, failureClass, remediation) {
return toolError(code, message, remediation, failureClass);
}
export function mapToolFailure(err) {
const e = err;
const raw = e?.message ? String(e.message) : "authorization_failed";
const lower = raw.toLowerCase();
const codeFromErr = typeof e?.code === "string" ? e.code : undefined;
const bodyText = typeof e?.bodyText === "string" ? e.bodyText : "";
const contentType = typeof e?.contentType === "string" ? e.contentType : "";
const jsonBody = bodyText ? parseJsonObject(bodyText) : null;
const jsonCode = jsonBody && typeof (jsonBody.error ?? jsonBody.code) === "string"
? String(jsonBody.error ?? jsonBody.code)
: "";
if (codeFromErr === "missing_api_key" || (lower.includes("api key") && !jsonBody)) {
return classifiedToolError("missing_api_key", "TRIGGUARD_API_KEY is required for authorize_action", "AUTHENTICATION_ERROR", "Set TRIGGUARD_API_KEY in the MCP server env. Never pass keys in tool arguments.");
}
if (codeFromErr === "missing_organization" ||
lower.includes("organizationid is required") ||
lower.includes("organization id")) {
return classifiedToolError("missing_organization", "TRIGGUARD_ORG_ID is required for live API-key authorization", "ORGANIZATION_BINDING_ERROR", "Set TRIGGUARD_ORG_ID or TRIGGUARD_ORGANIZATION_ID. Do not invent a default org.");
}
if (codeFromErr === "org_binding_conflict") {
return classifiedToolError("org_binding_conflict", "TRIGGUARD_ORGANIZATION_ID and TRIGGUARD_ORG_ID disagree", "ORGANIZATION_BINDING_ERROR", "Set exactly one org id, or make both values identical.");
}
if (codeFromErr === "unknown_decision" || lower.includes("unknown_decision")) {
return classifiedToolError("unknown_decision", "Gateway returned a non-canonical decision; fail-closed (not PERMIT)", "TRIGGUARD_APPLICATION_ERROR", "Retry with health_check. Do not execute. Canonical states: PERMIT, DENY, SILENCE, ESCALATE.");
}
if (looksLikeHtml(bodyText || raw, contentType)) {
return classifiedToolError("edge_html_error", "Upstream returned HTML instead of a TrigGuard authorize payload", "EDGE_HTML_ERROR", "This is not a policy decision. Check the gateway URL and edge/WAF path. Do not execute.");
}
const httpStatus = typeof e?.status === "number"
? e.status
: /HTTP (401|403|404|5\d\d)/.exec(raw)?.[1]
? Number(/HTTP (401|403|404|5\d\d)/.exec(raw)?.[1])
: undefined;
if (httpStatus === 401 || jsonCode === "TG_API_KEY_MISSING" || jsonCode === "TG_API_KEY_INVALID") {
return classifiedToolError("authentication_error", "Gateway rejected authentication (not a policy DENY)", "AUTHENTICATION_ERROR", "Check TRIGGUARD_API_KEY. Missing or invalid keys are not authorization decisions.");
}
if (looksLikeTrigGuardJson(bodyText) && httpStatus && httpStatus >= 400) {
const appCode = jsonCode && /^[A-Za-z0-9_]+$/.test(jsonCode) ? jsonCode.toLowerCase() : "trigguard_application_error";
return classifiedToolError(appCode.startsWith("tg_") ? "trigguard_application_error" : appCode, "TrigGuard returned an application error (not a four-state decision)", "TRIGGUARD_APPLICATION_ERROR", "Inspect error.code. Do not treat this as PERMIT or as a policy DENY.");
}
if (httpStatus === 403) {
return classifiedToolError("upstream_proxy_error", "HTTP 403 without a TrigGuard JSON decision — not credentials, not policy", "UPSTREAM_PROXY_ERROR", "Do not claim the API key or plan failed. Confirm the gateway JSON path is reachable.");
}
if (httpStatus === 404 || lower.includes("unsupported surface") || lower.includes("unknown_surface")) {
return classifiedToolError("unsupported_surface", "Surface is not registered or not allowed", "TRIGGUARD_APPLICATION_ERROR", "Call list_surfaces and retry with a registered surface id.");
}
if (lower.includes("abort") || lower.includes("timeout") || e?.code === "timeout") {
return classifiedToolError("timeout", "Gateway request timed out", "NETWORK_ERROR", "Retry once. Duplicate authorize calls are not automatically retried by this server.");
}
if ((typeof e?.status === "number" && e.status >= 500) ||
lower.includes("econnrefused") ||
lower.includes("enotfound") ||
lower.includes("fetch failed") ||
lower.includes("unavailable") ||
e?.code === "ECONNREFUSED" ||
e?.code === "ENOTFOUND") {
return classifiedToolError("network_error", "TrigGuard gateway is unreachable or failed closed", "NETWORK_ERROR", "Check TRIGGUARD_GATEWAY_URL and health_check. Do not execute.");
}
if (lower.includes("payload") || codeFromErr === "payload_too_large") {
return classifiedToolError("malformed_request", "Tool input exceeds size or schema limits", "TRIGGUARD_APPLICATION_ERROR", "Reduce context size. Do not send secrets in context.");
}
if (lower.includes("protocol mismatch") || codeFromErr === "protocol_mismatch") {
return classifiedToolError("protocol_mismatch", "Response was not a TrigGuard authorize payload", "UPSTREAM_PROXY_ERROR", "Confirm TRIGGUARD_GATEWAY_URL points at the execution gateway.");
}
if (lower.includes("verif") || codeFromErr === "receipt_verification_failed") {
return classifiedToolError("receipt_verification_failed", "Receipt could not be verified", "TRIGGUARD_APPLICATION_ERROR", "Do not treat the decision as trusted evidence. Call verify_receipt or tg verify.");
}
return classifiedToolError(codeFromErr && /^[a-z_]+$/.test(codeFromErr) ? codeFromErr : "authorization_failed", sanitizePublicMessage(raw), "TRIGGUARD_APPLICATION_ERROR", e?.remediation ?? "Call health_check. Do not execute without PERMIT.");
}
/** Canonical live-wire decisions. Unknown values fail closed — never PERMIT. */
export declare const CANONICAL_WIRE_DECISIONS: readonly ["PERMIT", "DENY", "SILENCE", "ESCALATE"];
export type CanonicalWireDecision = (typeof CANONICAL_WIRE_DECISIONS)[number];
export declare function isCanonicalWireDecision(value: string): value is CanonicalWireDecision;
/**
* Identity map only. SILENCE stays SILENCE. ESCALATE stays ESCALATE.
* BLOCK is not a wire state. Unknown is not PERMIT.
*/
export declare function assertCanonicalWireDecision(raw: string): CanonicalWireDecision;
//# sourceMappingURL=wireDecision.d.ts.map
{"version":3,"file":"wireDecision.d.ts","sourceRoot":"","sources":["../src/wireDecision.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,eAAO,MAAM,wBAAwB,oDAAqD,CAAC;AAE3F,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9E,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,qBAAqB,CAErF;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAM9E"}
/** Canonical live-wire decisions. Unknown values fail closed — never PERMIT. */
export const CANONICAL_WIRE_DECISIONS = ["PERMIT", "DENY", "SILENCE", "ESCALATE"];
export function isCanonicalWireDecision(value) {
return CANONICAL_WIRE_DECISIONS.includes(value);
}
/**
* Identity map only. SILENCE stays SILENCE. ESCALATE stays ESCALATE.
* BLOCK is not a wire state. Unknown is not PERMIT.
*/
export function assertCanonicalWireDecision(raw) {
const value = raw.trim().toUpperCase();
if (isCanonicalWireDecision(value))
return value;
throw Object.assign(new Error(`unknown_decision: ${value || "(empty)"}`), {
code: "unknown_decision",
});
}
# @trigguard/mcp-server 1.2.0 — Release summary
Publish-only bump so the npm tarball includes `mcpName` and aligned `server.json`.
## Why 1.2.0
Published `@trigguard/mcp-server@1.1.0` on npm does not contain:
```
mcpName=io.github.TrigGuard-AI/trigguard
```
This release ships that field in `package.json` and matching MCP registry metadata in `server.json`.
## Unchanged
- Authorization protocol (PERMIT, DENY, SILENCE, ESCALATE)
- Gateway, policy bundles, receipts, SDK, and CLI behavior
- Production policy hash is not modified by this package
## Four-state copy
TrigGuard supports four authorization states:
PERMIT
DENY
SILENCE
ESCALATE
PERMIT, DENY, and SILENCE are currently present in the active production policy bundle.
ESCALATE is protocol-supported and available for policy configurations that use approval workflows.
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "io.github.TrigGuard-AI/trigguard",
"title": "TrigGuard",
"description": "Execution authorization for AI agents and automated systems.",
"websiteUrl": "https://trigguardai.com",
"repository": {
"url": "https://github.com/TrigGuard-AI/TrigGuard",
"source": "github",
"subfolder": "packages/trigguard-mcp-server"
},
"version": "1.2.0",
"packages": [
{
"registryType": "npm",
"registryBaseUrl": "https://registry.npmjs.org",
"identifier": "@trigguard/mcp-server",
"version": "1.2.0",
"runtimeHint": "npx",
"transport": {
"type": "stdio"
},
"environmentVariables": [
{
"name": "TRIGGUARD_API_KEY",
"description": "TrigGuard gateway API key (tg_live_…)",
"isRequired": true,
"isSecret": true,
"format": "string"
},
{
"name": "TRIGGUARD_ORG_ID",
"description": "Organisation id sent as X-Consumer on POST /v1/authorize",
"isRequired": true,
"isSecret": false,
"format": "string"
},
{
"name": "TRIGGUARD_GATEWAY_URL",
"description": "Execution gateway origin (default https://api.trigguardai.com)",
"isRequired": false,
"isSecret": false,
"format": "string"
}
]
}
],
"_meta": {
"io.modelcontextprotocol.registry/publisher-provided": {
"npmPackage": "@trigguard/mcp-server",
"doNotPublish": "@trigguard/mcp",
"authorityPath": "MCP → @trigguard/agent-sdk → POST /v1/authorize",
"decisions": ["PERMIT", "DENY", "SILENCE", "ESCALATE"]
}
}
}
+3
-1
#!/usr/bin/env node
export {};
export declare function maybeHandleCli(argv?: readonly string[]): boolean;
/** True when this file is the process entrypoint, including macOS /tmp → /private/tmp and bin symlinks. */
export declare function isCliEntrypoint(argv1?: string | undefined, moduleUrl?: string): boolean;
//# sourceMappingURL=index.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAqCA,wBAAgB,cAAc,CAAC,IAAI,GAAE,SAAS,MAAM,EAA0B,GAAG,OAAO,CAUvF;AAED,2GAA2G;AAC3G,wBAAgB,eAAe,CAC7B,KAAK,GAAE,MAAM,GAAG,SAA2B,EAC3C,SAAS,GAAE,MAAwB,GAClC,OAAO,CAST"}
#!/usr/bin/env node
import { readFileSync, realpathSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { startStdioServer } from "./server.js";
startStdioServer().catch((err) => {
console.error("trigguard-mcp-server failed:", err);
process.exit(1);
});
const HELP = `trigguard-mcp-server — execution authorization for AI agents and automated systems
Usage:
npx @trigguard/mcp-server
trigguard-mcp-server [--help] [--version]
This process speaks MCP over stdio. Do not send logs to stdout.
Required for authorize_action:
TRIGGUARD_API_KEY
TRIGGUARD_ORG_ID (or TRIGGUARD_ORGANIZATION_ID)
Optional:
TRIGGUARD_GATEWAY_URL (default https://api.trigguardai.com)
Decisions: PERMIT | DENY | SILENCE | ESCALATE
TrigGuard authorizes execution. It does not execute actions.
Docs: https://github.com/TrigGuard-AI/TrigGuard/blob/main/docs/adoption/MCP_NPM_QUICKSTART.md
`;
function packageVersion() {
try {
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
return pkg.version ?? "1.2.0";
}
catch {
return "1.2.0";
}
}
export function maybeHandleCli(argv = process.argv.slice(2)) {
if (argv.includes("--help") || argv.includes("-h")) {
process.stdout.write(HELP);
return true;
}
if (argv.includes("--version") || argv.includes("-v")) {
process.stdout.write(`${packageVersion()}\n`);
return true;
}
return false;
}
/** True when this file is the process entrypoint, including macOS /tmp → /private/tmp and bin symlinks. */
export function isCliEntrypoint(argv1 = process.argv[1], moduleUrl = import.meta.url) {
if (!argv1)
return false;
try {
const argvReal = realpathSync(argv1);
const moduleReal = realpathSync(fileURLToPath(moduleUrl));
return argvReal === moduleReal;
}
catch {
return false;
}
}
if (isCliEntrypoint()) {
if (maybeHandleCli()) {
process.exit(0);
}
startStdioServer().catch((err) => {
console.error("trigguard-mcp-server failed:", err instanceof Error ? err.message : "unknown");
process.exit(1);
});
}

@@ -7,6 +7,6 @@ /** Structured MCP tool response helpers (stdio JSON content). */

readonly message: string;
readonly class?: string;
readonly remediation?: string;
};
};
export type ToolFailure = McpToolErrorBody;
export declare function jsonContent(data: unknown, opts?: {

@@ -21,7 +21,3 @@ readonly isError?: boolean;

};
export declare function toolError(code: string, message: string, remediation?: string): ReturnType<typeof jsonContent>;
export declare function isToolFailure(value: unknown): value is ToolFailure;
/** Wrap an async tool handler so thrown errors become structured MCP errors. */
export declare function withToolErrors<TArgs extends unknown[]>(fn: (...args: TArgs) => Promise<unknown>): (...args: TArgs) => Promise<ReturnType<typeof jsonContent>>;
export declare function stringFieldOrNull(value: unknown): string | null;
export declare function toolError(code: string, message: string, remediation?: string, failureClass?: string): ReturnType<typeof jsonContent>;
//# sourceMappingURL=mcpResponse.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"mcpResponse.d.ts","sourceRoot":"","sources":["../src/mcpResponse.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAEjE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;KAC/B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,gBAAgB,CAAC;AAE3C,wBAAgB,WAAW,CACzB,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACpC;IACD,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAQA;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,GACnB,UAAU,CAAC,OAAO,WAAW,CAAC,CAUhC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAQlE;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,KAAK,SAAS,OAAO,EAAE,EACpD,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,CAAC,GACvC,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAwB7D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAE/D"}
{"version":3,"file":"mcpResponse.d.ts","sourceRoot":"","sources":["../src/mcpResponse.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAEjE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;KAC/B,CAAC;CACH,CAAC;AAEF,wBAAgB,WAAW,CACzB,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACpC;IACD,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAQA;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,EACpB,YAAY,CAAC,EAAE,MAAM,GACpB,UAAU,CAAC,OAAO,WAAW,CAAC,CAWhC"}

@@ -11,3 +11,3 @@ /** Structured MCP tool response helpers (stdio JSON content). */

}
export function toolError(code, message, remediation) {
export function toolError(code, message, remediation, failureClass) {
const body = {

@@ -18,2 +18,3 @@ ok: false,

message,
...(failureClass ? { class: failureClass } : {}),
...(remediation ? { remediation } : {}),

@@ -24,30 +25,1 @@ },

}
export function isToolFailure(value) {
return Boolean(value &&
typeof value === "object" &&
value.ok === false &&
value.error &&
typeof value.error.code === "string");
}
/** Wrap an async tool handler so thrown errors become structured MCP errors. */
export function withToolErrors(fn) {
return async (...args) => {
try {
const result = await fn(...args);
if (isToolFailure(result)) {
return toolError(result.error.code, result.error.message, result.error.remediation);
}
return jsonContent(result);
}
catch (err) {
const message = err instanceof Error ? err.message : String(err);
if (/organizationId is required/i.test(message)) {
return toolError("organization_id_required", message, "Set TRIGGUARD_ORG_ID or TRIGGUARD_ORGANIZATION_ID (and TRIGGUARD_API_KEY). API key alone is not enough for authorize_action.");
}
return toolError("gateway_error", message, "Check TRIGGUARD_GATEWAY_URL, credentials, and network connectivity; then retry. Use health_check for diagnostics.");
}
};
}
export function stringFieldOrNull(value) {
return typeof value === "string" && value.trim().length > 0 ? value : null;
}

@@ -1,1 +0,1 @@

{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,OAAO,EAcL,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AAKpB,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAqR3E;AAED,wBAAsB,gBAAgB,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAK9E"}
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMpE,OAAO,EAcL,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AAKpB,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CA4S3E;AAED,wBAAsB,gBAAgB,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAK9E"}

@@ -5,12 +5,17 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

import * as z from "zod";
import { withToolErrors } from "./mcpResponse.js";
import { jsonContent, toolError } from "./mcpResponse.js";
import { enrichFailure, mapToolFailure, wrapFetchWithCapture } from "./errors.js";
import { handleAuthorizeAction, handleExplainDecision, handleGetPolicy, handleGetSurface, handleHealthCheck, handleListOrgPolicies, handleListPendingPolicyChanges, handleListSurfaces, handlePreviewOrgPolicyIntent, handleVerifyReceipt, loadMcpServerConfig, mcpServerPackageVersion, resolvePolicyPlatformConfig, } from "./tools.js";
const SESSION_REMEDIATION = "Set TRIGGUARD_SESSION_TOKEN (from tg login) and TRIGGUARD_ORG_ID. Gateway API key alone is not enough for org policy tools.";
export function createTrigGuardMcpServer(config) {
const { fetchImpl, capture } = wrapFetchWithCapture(config.fetchImpl);
const runtime = { ...config, fetchImpl };
const agent = createTrigGuardAgent({
gatewayUrl: config.gatewayUrl,
apiKey: config.apiKey,
organizationId: config.organizationId,
defaultActorId: config.defaultActorId,
gatewayUrl: runtime.gatewayUrl,
apiKey: runtime.apiKey,
organizationId: runtime.organizationId,
defaultActorId: runtime.defaultActorId,
fetchImpl,
});
const fail = (err) => mapToolFailure(enrichFailure(err, capture.last));
const server = new McpServer({

@@ -21,3 +26,3 @@ name: "trigguard-mcp-server",

server.registerTool("authorize_action", {
description: "Request TrigGuard authority for an action. Returns PERMIT, DENY, or SILENCE with execution receipt metadata and v1.1 explanation fields (reason_code, escalation_*, policy_version, evaluation_id). Requires TRIGGUARD_API_KEY and TRIGGUARD_ORG_ID (or TRIGGUARD_ORGANIZATION_ID).",
description: "Request TrigGuard authority for an action. Returns PERMIT, DENY, SILENCE, or ESCALATE with execution receipt metadata and v1.1 explanation fields (reason_code, escalation_*, policy_version, evaluation_id). SILENCE is withhold/no-commitment; ESCALATE is an approval lifecycle. Neither is PERMIT. Requires TRIGGUARD_API_KEY (gateway API key).",
inputSchema: {

@@ -38,3 +43,10 @@ surface: z.string().describe("Execution surface (e.g. deploy.release)"),

},
}, withToolErrors(async (input) => handleAuthorizeAction(agent, config, input)));
}, async (input) => {
try {
return jsonContent(await handleAuthorizeAction(agent, runtime, input));
}
catch (err) {
return fail(err);
}
});
server.registerTool("verify_receipt", {

@@ -56,5 +68,12 @@ description: "Verify a TrigGuard execution receipt by execution id. Optional include_receipt (default false) returns the receipt body. Requires gateway API key for authenticated lookup when the gateway enforces auth.",

},
}, withToolErrors(async (input) => handleVerifyReceipt(agent, input)));
}, async (input) => {
try {
return jsonContent(await handleVerifyReceipt(agent, input));
}
catch (err) {
return fail(err);
}
});
server.registerTool("explain_decision", {
description: "Explain a TrigGuard authority decision using an execution_id and/or a prior authorize_result payload. Returns structured reason_code, evidence_completeness, evidence_gaps, and next actions. Prefer passing authorize_result when available. Read-only — does not authorize.",
description: "Explain a TrigGuard authority decision using an execution_id and/or a prior authorize_result payload. Returns structured reason_code, missing requirements, and next actions. Read-only — does not authorize.",
inputSchema: {

@@ -74,3 +93,14 @@ execution_id: z.string().optional().describe("Execution id (exec_…) from authorize_action"),

},
}, withToolErrors(async (input) => handleExplainDecision(agent, input)));
}, async (input) => {
try {
const out = await handleExplainDecision(agent, input);
if (out && typeof out === "object" && "ok" in out && out.ok === false) {
return toolError(out.error.code, out.error.message, out.error.remediation);
}
return jsonContent(out);
}
catch (err) {
return fail(err);
}
});
server.registerTool("get_surface", {

@@ -88,3 +118,10 @@ description: "Read execution surface registry metadata for one surface id (read-only, no policy evaluation).",

},
}, withToolErrors(async (input) => handleGetSurface(config, input)));
}, async (input) => {
try {
return jsonContent(await handleGetSurface(runtime, input));
}
catch (err) {
return fail(err);
}
});
server.registerTool("list_surfaces", {

@@ -103,5 +140,12 @@ description: "List registered execution surfaces from the gateway well-known registry (or local registry override). Read-only discovery.",

},
}, withToolErrors(async (input) => handleListSurfaces(config, input)));
}, async (input) => {
try {
return jsonContent(await handleListSurfaces(runtime, input));
}
catch (err) {
return fail(err);
}
});
server.registerTool("get_policy", {
description: "Read human-readable policy metadata for a surface from the bundled policy artifact. Does NOT evaluate live org policy — call authorize_action for PERMIT/DENY/SILENCE.",
description: "Read human-readable policy metadata for a surface from the bundled policy artifact. Does NOT evaluate live org policy — call authorize_action for PERMIT/DENY/SILENCE/ESCALATE.",
inputSchema: {

@@ -117,3 +161,3 @@ surface: z.string().describe("Surface id"),

},
}, withToolErrors(async (input) => handleGetPolicy(input)));
}, async (input) => jsonContent(handleGetPolicy(input)));
server.registerTool("list_org_policies", {

@@ -129,16 +173,9 @@ description: "List org policies from the TrigGuard control plane Policy Platform. Requires TRIGGUARD_SESSION_TOKEN + TRIGGUARD_ORG_ID (session JWT rail — not gateway API key alone). Read-only.",

},
}, withToolErrors(async () => {
}, async () => {
const resolved = resolvePolicyPlatformConfig();
if (!resolved.ok) {
return {
ok: false,
error: {
code: "session_auth_required",
message: resolved.error,
remediation: SESSION_REMEDIATION,
},
};
return toolError("session_auth_required", resolved.error, SESSION_REMEDIATION);
}
return handleListOrgPolicies(resolved.config);
}));
return jsonContent(await handleListOrgPolicies(resolved.config));
});
server.registerTool("list_pending_policy_changes", {

@@ -154,16 +191,9 @@ description: "List pending policy change requests awaiting approval. Requires TRIGGUARD_SESSION_TOKEN + TRIGGUARD_ORG_ID. Read-only.",

},
}, withToolErrors(async () => {
}, async () => {
const resolved = resolvePolicyPlatformConfig();
if (!resolved.ok) {
return {
ok: false,
error: {
code: "session_auth_required",
message: resolved.error,
remediation: SESSION_REMEDIATION,
},
};
return toolError("session_auth_required", resolved.error, SESSION_REMEDIATION);
}
return handleListPendingPolicyChanges(resolved.config);
}));
return jsonContent(await handleListPendingPolicyChanges(resolved.config));
});
server.registerTool("preview_policy_intent", {

@@ -198,18 +228,11 @@ description: "Compile-only preview of a policy intent against the Policy Platform. Requires TRIGGUARD_SESSION_TOKEN + TRIGGUARD_ORG_ID. Does not persist. Not a substitute for authorize_action.",

},
}, withToolErrors(async (input) => {
}, async (input) => {
const resolved = resolvePolicyPlatformConfig();
if (!resolved.ok) {
return {
ok: false,
error: {
code: "session_auth_required",
message: resolved.error,
remediation: SESSION_REMEDIATION,
},
};
return toolError("session_auth_required", resolved.error, SESSION_REMEDIATION);
}
return handlePreviewOrgPolicyIntent(resolved.config, input);
}));
return jsonContent(await handlePreviewOrgPolicyIntent(resolved.config, input));
});
server.registerTool("health_check", {
description: "Check TrigGuard gateway reachability, policy engine status, registry hash, and whether API key / org id / session JWT env vars are present (values are never returned).",
description: "Check TrigGuard gateway reachability, policy engine status, registry hash, and whether API key / session JWT env vars are present (values are never returned).",
inputSchema: {

@@ -228,3 +251,10 @@ detailed: z

},
}, withToolErrors(async (input) => handleHealthCheck(config, input)));
}, async (input) => {
try {
return jsonContent(await handleHealthCheck(runtime, input));
}
catch (err) {
return fail(err);
}
});
return server;

@@ -231,0 +261,0 @@ }

@@ -5,3 +5,2 @@ import type { TrigGuardAgent } from "@trigguard/agent-sdk";

import { type FetchFn } from "./gatewayRegistry.js";
import type { ToolFailure } from "./mcpResponse.js";
import { type PolicyPlatformConfig, type PreviewPolicyIntentInput } from "./policyPlatform.js";

@@ -17,9 +16,17 @@ export type McpServerConfig = {

};
/**
* Organisation precedence: TRIGGUARD_ORGANIZATION_ID, then TRIGGUARD_ORG_ID,
* then TRIGGUARD_ACTIVE_ORG_ID. Conflicting explicit values fail closed.
*/
export declare function resolveOrganizationIdFromEnv(env?: NodeJS.ProcessEnv): {
ok: true;
organizationId?: string;
} | {
ok: false;
code: string;
message: string;
};
export declare function loadMcpServerConfig(): McpServerConfig;
export declare function assertAuthorizeCredentials(config: McpServerConfig): void;
export declare function mcpServerPackageVersion(): string;
/**
* Fail closed before calling the gateway when API-key authorize cannot satisfy
* execution-sdk organization binding. Never throws.
*/
export declare function validateAuthorizeConfig(config: McpServerConfig): ToolFailure | null;
export type AuthorizeActionInput = {

@@ -35,2 +42,3 @@ readonly surface: string;

readonly verify_url: string | null;
/** v1.1 additive fields (always present; null when gateway omitted them). */
readonly reason_code: string | null;

@@ -41,6 +49,13 @@ readonly escalation_required: boolean;

readonly evaluation_id: string | null;
/** Receipt metadata preserved from the gateway. Offline Ed25519 verify stays in execution-sdk / tg verify. */
readonly protocol_version: string | null;
readonly surface: string | null;
readonly authority_key_id: string | null;
readonly receipt_present: boolean;
readonly signature_present: boolean;
};
export declare function handleAuthorizeAction(agent: TrigGuardAgent, config: McpServerConfig, input: AuthorizeActionInput): Promise<AuthorizeActionOutput | ToolFailure>;
export declare function handleAuthorizeAction(agent: TrigGuardAgent, config: McpServerConfig, input: AuthorizeActionInput): Promise<AuthorizeActionOutput>;
export type VerifyReceiptInput = {
readonly execution_id: string;
/** When true, include the receipt body from gateway lookup (v1.1; default false). */
readonly include_receipt?: boolean;

@@ -94,5 +109,13 @@ };

readonly execution_id?: string;
/** Optional pass-through of a prior authorize / gateway body still in agent context. */
readonly authorize_result?: Record<string, unknown>;
};
export declare function handleExplainDecision(agent: TrigGuardAgent, input: ExplainDecisionInput): Promise<ExplainDecisionOutput | ToolFailure>;
export declare function handleExplainDecision(agent: TrigGuardAgent, input: ExplainDecisionInput): Promise<ExplainDecisionOutput | {
ok: false;
error: {
code: string;
message: string;
remediation?: string;
};
}>;
export type HealthCheckInput = {

@@ -115,3 +138,2 @@ readonly detailed?: boolean;

readonly session_token_present: boolean;
readonly organization_id_present: boolean;
};

@@ -118,0 +140,0 @@ readonly details?: unknown;

@@ -1,1 +0,1 @@

{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,2BAA2B,EAAyB,MAAM,6BAA6B,CAAC;AAEjG,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAwB,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,EAKL,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC9B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,wBAAgB,mBAAmB,IAAI,eAAe,CAerD;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAQhD;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,GAAG,IAAI,CAyBnF;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,qBAAqB,GAAG,WAAW,CAAC,CAmC9C;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAe9B;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IACvE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;CACtC,CAAC;AAEF,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,gBAAgB,CAAC,CA0B3B;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B,CAAC;AAQF,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,eAAe,EACvB,KAAK,GAAE,iBAAsB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CAkC7B;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,sDAEpD;AAED,wBAAgB,2BAA2B,IACvC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,GAC1C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAI/B;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,CAE1F;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,oBAAoB,EAC5B,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrD,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,qBAAqB,GAAG,WAAW,CAAC,CA6E9C;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,IAAI,GAAG,UAAU,GAAG,aAAa,CAAC;IAC3D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,uBAAuB,EAAE,OAAO,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE;QAClB,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;QAClC,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAC;QACxC,QAAQ,CAAC,uBAAuB,EAAE,OAAO,CAAC;KAC3C,CAAC;IACF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,eAAe,EACvB,KAAK,GAAE,gBAAqB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAqF5B"}
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}

@@ -5,9 +5,32 @@ import { readFileSync } from "node:fs";

import { BUILTIN_SURFACE_DEFINITIONS, createSurfaceRegistry } from "@trigguard/surface-registry";
import { recallAuthorizeEvidence, rememberAuthorizeEvidence } from "./authorizeCache.js";
import { buildExplainDecision, extractAuthorizationEnrichment, } from "./decisionExplain.js";
import { fetchGatewayRegistry } from "./gatewayRegistry.js";
import { stringFieldOrNull } from "./mcpResponse.js";
import { loadPolicyMetadataForSurface } from "./policyMetadata.js";
import { listOrgPolicies, listPendingOrgPolicyChanges, loadPolicyPlatformConfig, previewOrgPolicyIntent, } from "./policyPlatform.js";
import { codedError } from "./errors.js";
import { assertCanonicalWireDecision } from "./wireDecision.js";
const MAX_CONTEXT_JSON_BYTES = 64 * 1024;
/**
* Organisation precedence: TRIGGUARD_ORGANIZATION_ID, then TRIGGUARD_ORG_ID,
* then TRIGGUARD_ACTIVE_ORG_ID. Conflicting explicit values fail closed.
*/
export function resolveOrganizationIdFromEnv(env = process.env) {
const a = env.TRIGGUARD_ORGANIZATION_ID?.trim() || "";
const b = env.TRIGGUARD_ORG_ID?.trim() || "";
const c = env.TRIGGUARD_ACTIVE_ORG_ID?.trim() || "";
if (a && b && a !== b) {
return {
ok: false,
code: "org_binding_conflict",
message: "TRIGGUARD_ORGANIZATION_ID and TRIGGUARD_ORG_ID disagree",
};
}
const organizationId = a || b || c || undefined;
return { ok: true, organizationId };
}
export function loadMcpServerConfig() {
const org = resolveOrganizationIdFromEnv();
if (!org.ok) {
throw codedError(org.code, org.message);
}
const gatewayUrl = process.env.TRIGGUARD_GATEWAY_URL?.trim() ||

@@ -17,10 +40,20 @@ process.env.TRIGGUARD_ENDPOINT?.trim() ||

const apiKey = process.env.TRIGGUARD_API_KEY?.trim() || undefined;
const organizationId = process.env.TRIGGUARD_ORGANIZATION_ID?.trim() ||
process.env.TRIGGUARD_ORG_ID?.trim() ||
process.env.TRIGGUARD_ACTIVE_ORG_ID?.trim() ||
undefined;
const defaultActorId = process.env.TRIGGUARD_MCP_ACTOR_ID?.trim() || "trigguard-mcp-server";
const registryPath = process.env.TRIGGUARD_SURFACE_REGISTRY_PATH?.trim() || undefined;
return { gatewayUrl, apiKey, organizationId, defaultActorId, registryPath };
return {
gatewayUrl,
apiKey,
organizationId: org.organizationId,
defaultActorId,
registryPath,
};
}
export function assertAuthorizeCredentials(config) {
if (!config.apiKey) {
throw codedError("missing_api_key", "TRIGGUARD_API_KEY is required for authorize_action");
}
if (!config.organizationId) {
throw codedError("missing_organization", "organizationId is required for production API-key authorization");
}
}
export function mcpServerPackageVersion() {

@@ -30,39 +63,16 @@ try {

const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
return typeof pkg.version === "string" ? pkg.version : "1.1.0";
return typeof pkg.version === "string" ? pkg.version : "1.2.0";
}
catch {
return "1.1.0";
return "1.2.0";
}
}
/**
* Fail closed before calling the gateway when API-key authorize cannot satisfy
* execution-sdk organization binding. Never throws.
*/
export function validateAuthorizeConfig(config) {
if (config.apiKey && !config.organizationId) {
return {
ok: false,
error: {
code: "organization_id_required",
message: "TRIGGUARD_ORG_ID (or TRIGGUARD_ORGANIZATION_ID) is required when TRIGGUARD_API_KEY is set",
remediation: "Export TRIGGUARD_ORG_ID from tg whoami / console, alongside TRIGGUARD_API_KEY. API key alone cannot authorize.",
},
};
export async function handleAuthorizeAction(agent, config, input) {
assertAuthorizeCredentials(config);
if (input.context) {
const bytes = Buffer.byteLength(JSON.stringify(input.context), "utf8");
if (bytes > MAX_CONTEXT_JSON_BYTES) {
throw codedError("payload_too_large", "context exceeds 64 KiB");
}
}
if (!config.apiKey) {
return {
ok: false,
error: {
code: "api_key_required",
message: "TRIGGUARD_API_KEY is required for authorize_action",
remediation: "Set TRIGGUARD_API_KEY (tg_live_…) and TRIGGUARD_ORG_ID in the MCP server environment (not in chat).",
},
};
}
return null;
}
export async function handleAuthorizeAction(agent, config, input) {
const invalid = validateAuthorizeConfig(config);
if (invalid)
return invalid;
const decision = await agent.authorize({

@@ -74,2 +84,3 @@ surface: input.surface,

});
const wire = assertCanonicalWireDecision(decision.label());
const enrichment = extractAuthorizationEnrichment({

@@ -80,4 +91,12 @@ body: decision.raw.body,

});
const out = {
decision: decision.label(),
const body = decision.raw.body && typeof decision.raw.body === "object"
? decision.raw.body
: {};
const receipt = decision.receipt;
const protocolVersion = (typeof body.protocol === "string" && body.protocol) ||
(typeof receipt?.protocolVersion === "string"
? receipt.protocolVersion
: null);
return {
decision: wire,
execution_id: decision.executionId ?? null,

@@ -87,13 +106,8 @@ receipt_hash: decision.receipt?.receiptHash ?? null,

...enrichment,
protocol_version: protocolVersion || null,
surface: receipt?.surface ?? input.surface,
authority_key_id: receipt?.authorityKeyId ?? null,
receipt_present: Boolean(receipt),
signature_present: Boolean(receipt?.authoritySignature),
};
const rawBody = decision.raw.body && typeof decision.raw.body === "object" && !Array.isArray(decision.raw.body)
? decision.raw.body
: {};
rememberAuthorizeEvidence(out.execution_id, {
enrichment,
body: { ...rawBody, ...out },
decision: out.decision,
surface: input.surface,
});
return out;
}

@@ -107,4 +121,4 @@ export async function handleVerifyReceipt(agent, input) {

valid: result.valid,
decision: receipt ? stringFieldOrNull(receipt.decision) : null,
surface: receipt ? stringFieldOrNull(receipt.surface) : null,
decision: receipt ? String(receipt.decision ?? null) : null,
surface: receipt ? String(receipt.surface ?? null) : null,
};

@@ -142,10 +156,5 @@ if (input.include_receipt) {

}
function coerceLimit(limitRaw, fallback = 50) {
const n = typeof limitRaw === "number" ? limitRaw : Number(limitRaw);
if (!Number.isFinite(n))
return fallback;
return Math.min(Math.max(1, Math.floor(n)), 200);
}
export async function handleListSurfaces(config, input = {}) {
const limit = coerceLimit(input.limit, 50);
const limitRaw = input.limit ?? 50;
const limit = Math.min(Math.max(1, Math.floor(limitRaw)), 200);
const prefix = input.prefix?.trim() || "";

@@ -226,9 +235,7 @@ let surfaces;

surface: typeof body.surface === "string" ? body.surface : null,
fromAuthorizeResult: true,
});
}
const executionId = input.execution_id;
const cached = recallAuthorizeEvidence(executionId);
const verified = await agent.verifyExecution(executionId);
if (!verified.receipt && !cached) {
if (!verified.receipt) {
return {

@@ -239,30 +246,10 @@ ok: false,

message: `No receipt found for ${executionId}`,
remediation: "Confirm the execution_id from authorize_action and that the gateway ledger retained it. If authorize just succeeded in this session, retry explain_decision with authorize_result.",
remediation: "Confirm the execution_id from authorize_action and that the gateway ledger retained it.",
},
};
}
const receipt = verified.receipt && typeof verified.receipt === "object"
? verified.receipt
: {};
const mergedBody = {
execution_id: executionId,
receipt,
...(cached?.body ?? {}),
};
if (cached?.enrichment.reason_code && !mergedBody.reason_code) {
mergedBody.reason_code = cached.enrichment.reason_code;
}
if (cached?.enrichment.policy_version && !mergedBody.policy_bundle_version) {
mergedBody.policy_bundle_version = cached.enrichment.policy_version;
}
if (cached?.enrichment.escalation_required && !mergedBody.escalation && cached.body.escalation) {
mergedBody.escalation = cached.body.escalation;
}
return buildExplainDecision({
body: mergedBody,
receipt,
body: { execution_id: executionId, receipt: verified.receipt },
receipt: verified.receipt,
executionId,
decision: cached?.decision ?? null,
surface: cached?.surface ?? null,
fromSessionCache: Boolean(cached),
});

@@ -343,3 +330,2 @@ }

session_token_present: sessionPresent,
organization_id_present: Boolean(config.organizationId),
},

@@ -346,0 +332,0 @@ };

{
"server": "trigguard-mcp-server",
"version": "1.1.0",
"version": "1.2.0",
"package": "@trigguard/mcp-server",
"transport": "stdio",
"authority_path": "MCP → @trigguard/agent-sdk → POST /v1/execute",
"authority_path": "MCP → @trigguard/agent-sdk → POST /v1/authorize",
"semver_policy": "Additive minors (1.x). Breaking renames/removals require 2.0.0.",
"auth_rails": {
"gateway_api_key": {
"env": ["TRIGGUARD_API_KEY"],
"env": ["TRIGGUARD_API_KEY", "TRIGGUARD_ORG_ID"],
"tools": ["authorize_action", "verify_receipt", "explain_decision", "get_surface", "list_surfaces", "health_check"]

@@ -23,3 +23,3 @@ },

"mutates_reality": false,
"description": "Request TrigGuard authority for an action. Returns PERMIT, DENY, or SILENCE with execution receipt metadata and v1.1 explanation fields. Requires TRIGGUARD_API_KEY and TRIGGUARD_ORG_ID.",
"description": "Request TrigGuard authority for an action. Returns PERMIT, DENY, SILENCE, or ESCALATE with execution receipt metadata and v1.1 explanation fields.",
"parameters": {

@@ -31,3 +31,3 @@ "surface": { "type": "string", "required": true, "example": "deploy.release" },

"returns": {
"decision": { "enum": ["PERMIT", "DENY", "SILENCE"] },
"decision": { "enum": ["PERMIT", "DENY", "SILENCE", "ESCALATE"] },
"execution_id": { "type": "string", "nullable": true },

@@ -214,5 +214,5 @@ "receipt_hash": { "type": "string", "nullable": true },

"READ_ONLY": "No authority decision; safe discovery",
"AUTHORIZATION": "Returns PERMIT/DENY/SILENCE — call before mutating actions",
"AUTHORIZATION": "Returns PERMIT/DENY/SILENCE/ESCALATE — call before mutating actions",
"VERIFICATION": "Validates existing receipt evidence"
}
}
{
"name": "@trigguard/mcp-server",
"version": "1.1.0",
"description": "Production-grade TrigGuard MCP stdio server — authority via @trigguard/agent-sdk only",
"version": "1.2.0",
"description": "MCP stdio server for TrigGuard execution authorization (PERMIT/DENY/SILENCE/ESCALATE)",
"mcpName": "io.github.TrigGuard-AI/trigguard",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"bin": {
"trigguard-mcp-server": "dist/index.js"
"trigguard-mcp-server": "./dist/index.js"
},

@@ -63,5 +64,6 @@ "exports": {

"README.md",
"LICENSE",
"server.json",
"mcp-tools-metadata.json",
"RELEASE_NOTES_v1.1.0.md",
"RELEASE_NOTES_v1.2.0.md",
"MIGRATION_v1.1.0.md"

@@ -68,0 +70,0 @@ ],

+66
-89
# @trigguard/mcp-server
Production-grade **stdio MCP server** for TrigGuard authority.
**Execution authorization for AI agents and automated systems.**
**Version:** 1.1.0 (backwards-compatible additive release over the 7-tool v1.0 surface)
TrigGuard authorizes execution. It does not execute actions. This package is a stdio MCP transport: tools request authority and return **PERMIT · DENY · SILENCE · ESCALATE** plus a signed receipt. Policy stays on the gateway.
Transport only — all decisions flow through:
```
MCP tool → @trigguard/agent-sdk → POST /v1/execute
MCP client → @trigguard/mcp-server → @trigguard/agent-sdk → POST /v1/authorize
```
**Decision model:** PERMIT · DENY · SILENCE (no ESCALATE wire state for agents — escalation metadata is returned as explanation fields)
Do not install `@trigguard/mcp` (abandoned scaffold).
## Install (npm)
## Install
```bash
npm install -g @trigguard/mcp-server
export TRIGGUARD_API_KEY=tg_live_…
npx @trigguard/mcp-server --help
npx @trigguard/mcp-server
```
For application code (non-MCP), use [`trigguard`](../../packages/trigguard-sdk/README.md) — `npm install trigguard`.
Or: `npm install -g @trigguard/mcp-server`
Full MCP setup: [MCP npm quickstart](../../docs/adoption/MCP_NPM_QUICKSTART.md)
## 30-second config (Cursor)
## Authentication (two rails)
| Rail | Env vars | Tools |
|------|----------|-------|
| **Gateway API key** | `TRIGGUARD_API_KEY` + `TRIGGUARD_ORG_ID` (or `TRIGGUARD_ORGANIZATION_ID`) (+ optional `TRIGGUARD_GATEWAY_URL`) | `authorize_action`, `verify_receipt`, `explain_decision`, `get_surface`, `list_surfaces`, `health_check` |
| **Session JWT (org / Policy Platform)** | `TRIGGUARD_SESSION_TOKEN` + `TRIGGUARD_ORG_ID` (+ optional `TRIGGUARD_CONTROL_PLANE_URL`) | `list_org_policies`, `list_pending_policy_changes`, `preview_policy_intent` |
- API key alone is **not** enough for org policy tools.
- Session JWT alone is **not** enough for `authorize_action`.
- Credentials stay in the MCP server process — never pass them in LLM tool arguments.
Obtain a session via `tg login`, then export the token / org id (see `tg whoami`).
## Tools
| Tool | Auth rail | Purpose |
|------|-----------|---------|
| `authorize_action` | API key | Governed PERMIT / DENY / SILENCE + v1.1 explanation fields |
| `verify_receipt` | API key | Lookup / verify by execution id; optional `include_receipt` |
| `explain_decision` | API key | Structured “why” for a decision (`execution_id` and/or `authorize_result`) |
| `get_surface` | Public registry | One surface’s registry metadata (read-only) |
| `list_surfaces` | Public registry | Discover registered surface ids |
| `get_policy` | Bundled artifact | Human-readable **metadata only** — not live evaluation |
| `list_org_policies` | Session JWT | List org policies (read-only) |
| `list_pending_policy_changes` | Session JWT | Pending change requests (read-only) |
| `preview_policy_intent` | Session JWT | Compile-only preview — does not persist |
| `health_check` | None / optional key | Gateway reachability + policy engine status |
**Not MCP tools (by design):** policy create / approve / activate, audit history browse. Use `tg policy …` or the console.
### `authorize_action` response (v1.1 additive fields)
Existing fields are unchanged. New fields are always present (nullable):
| Field | Meaning |
|-------|---------|
| `reason_code` | Public gateway reason code when available |
| `escalation_required` | `true` when escalation metadata / ESCALATE evidence is present |
| `escalation_reason` | Reason associated with escalation when present |
| `policy_version` | Policy bundle version when available |
| `evaluation_id` | Decision / evaluation id when available (often same as `execution_id`) |
### `verify_receipt` (v1.1)
| Parameter | Default | Meaning |
|-----------|---------|---------|
| `include_receipt` | `false` | When `true`, include the receipt document under `receipt` |
## Cursor / Claude configuration
```json

@@ -80,6 +28,8 @@ {

"trigguard": {
"command": "trigguard-mcp-server",
"command": "npx",
"args": ["-y", "@trigguard/mcp-server"],
"env": {
"TRIGGUARD_GATEWAY_URL": "https://api.trigguardai.com",
"TRIGGUARD_API_KEY": "${env:TRIGGUARD_API_KEY}"
"TRIGGUARD_API_KEY": "${env:TRIGGUARD_API_KEY}",
"TRIGGUARD_ORG_ID": "${env:TRIGGUARD_ORG_ID}"
}

@@ -91,43 +41,70 @@ }

For org policy tools, also set `TRIGGUARD_SESSION_TOKEN` and `TRIGGUARD_ORG_ID` in the MCP server env.
Claude Desktop uses the same `command` / `args` / `env` under `mcpServers` in `claude_desktop_config.json`.
## Environment
## One authorization example
| Variable | Purpose |
|----------|---------|
| `TRIGGUARD_GATEWAY_URL` | Gateway base URL (default: `https://api.trigguardai.com`) |
| `TRIGGUARD_API_KEY` | API key (`tg_live_…`) — required for `authorize_action` |
| `TRIGGUARD_ORG_ID` / `TRIGGUARD_ORGANIZATION_ID` | Organisation id for API-key authorize (`X-Consumer`) and org policy tools |
| `TRIGGUARD_MCP_ACTOR_ID` | Actor id for authorize calls (default: `trigguard-mcp-server`) |
| `TRIGGUARD_SURFACE_REGISTRY_PATH` | Optional local registry override (dev only) |
| `TRIGGUARD_POLICY_BUNDLE_PATH` | Optional local policy metadata override (dev only) |
| `TRIGGUARD_SESSION_TOKEN` | Control-plane session JWT for org policy tools |
| `TRIGGUARD_ORG_ID` | Active org / workspace id for org policy tools |
| `TRIGGUARD_CONTROL_PLANE_URL` | Control plane base (default: `https://control.trigguardai.com`) |
Ask the host:
## Errors
> Use TrigGuard `authorize_action` for surface `deploy.release` with repository `TrigGuard-AI/TrigGuard`.
Auth / validation failures return MCP `isError: true` with a structured body:
Typical PERMIT payload (shape):
```json
{
"ok": false,
"error": {
"code": "session_auth_required",
"message": "…",
"remediation": "…"
}
"decision": "PERMIT",
"execution_id": "exec_…",
"receipt_hash": "…",
"verify_url": "https://api.trigguardai.com/verify/exec_…"
}
```
Then call `verify_receipt` with that `execution_id`.
## Four-state semantics
TrigGuard supports four authorization states:
PERMIT
DENY
SILENCE
ESCALATE
PERMIT, DENY, and SILENCE are currently present in the active production policy bundle.
ESCALATE is protocol-supported and available for policy configurations that use approval workflows.
| Decision | Meaning | May the caller execute? |
|----------|---------|-------------------------|
| **PERMIT** | Authorized | Yes, the **caller** executes |
| **DENY** | Refused | No |
| **SILENCE** | No authorization commitment | No (not an approval queue) |
| **ESCALATE** | Policy-required intervention | No until released |
Unknown gateway values fail closed. They are never mapped to PERMIT. SILENCE is not aliased to ESCALATE. SILENCE does not mean approval and does not automatically escalate. ESCALATE is protocol-supported; it is not claimed as live in the current production policy bundle.
## Receipt verification
`verify_receipt` looks up the execution on the gateway. Offline Ed25519 verification of a receipt body is `tg verify` / `@trigguard/execution-sdk` after you hold trusted keys.
## Security model
- No local policy evaluation
- No default organisation
- `TRIGGUARD_API_KEY` + `TRIGGUARD_ORG_ID` required for `authorize_action`
- Credentials live in MCP server env — never in tool arguments
- This server does not actuate robots, deploys, or payments
Robotics surfaces (`robot.motion.execute`, `robot.actuator.command`, `robot.zone.enter`, `robot.emergency.stop.override`) are authorization-only in this transport.
## Tools
`authorize_action` · `verify_receipt` · `explain_decision` · `get_surface` · `list_surfaces` · `get_policy` · `health_check` · org policy read tools (session JWT)
## Monorepo development
Contributor-only:
```bash
npm run build -w @trigguard/mcp-server
TRIGGUARD_API_KEY=tg_live_… npm run start -w @trigguard/mcp-server
npm test -w @trigguard/mcp-server
```
## Release notes
See [RELEASE_NOTES_v1.1.0.md](./RELEASE_NOTES_v1.1.0.md) and [MIGRATION_v1.1.0.md](./MIGRATION_v1.1.0.md).