@polygraphso/litmus 0.8.1 → 0.9.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 +58 -0
- package/dist/{chunk-ZR6XRGMQ.js → chunk-44R4ZYOE.js} +67 -0
- package/dist/{chunk-VOPISHBU.js → chunk-BUKDFSDO.js} +2 -2
- package/dist/{chunk-35UOPCBW.js → chunk-RYJXVMCT.js} +482 -9
- package/dist/chunk-Z66GKAQD.js +692 -0
- package/dist/cli-skill.d.ts +1 -0
- package/dist/cli-skill.js +98 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +437 -2
- package/dist/index.js +86 -8
- package/dist/mcp.js +130 -122
- package/dist/src-TMJOIVGB.js +67 -0
- package/package.json +4 -3
- package/dist/chunk-BPS4YCDL.js +0 -250
- package/dist/src-RSTPCEYU.js +0 -31
package/dist/index.js
CHANGED
|
@@ -1,49 +1,88 @@
|
|
|
1
1
|
import {
|
|
2
2
|
LITMUS_SCHEMA,
|
|
3
|
+
LITMUS_SKILL_SCHEMA,
|
|
3
4
|
NETWORKS,
|
|
4
5
|
RUN_LITMUS_TOOL_DESCRIPTION,
|
|
5
6
|
RUN_LITMUS_TOOL_NAME,
|
|
6
7
|
RUN_LITMUS_TOOL_TITLE,
|
|
8
|
+
RUN_SKILL_LITMUS_TOOL_DESCRIPTION,
|
|
9
|
+
RUN_SKILL_LITMUS_TOOL_NAME,
|
|
10
|
+
RUN_SKILL_LITMUS_TOOL_TITLE,
|
|
11
|
+
VERIFY_SKILL_TOOL_DESCRIPTION,
|
|
12
|
+
VERIFY_SKILL_TOOL_NAME,
|
|
13
|
+
VERIFY_SKILL_TOOL_TITLE,
|
|
7
14
|
decodeLitmusAttestation,
|
|
15
|
+
decodeSkillAttestation,
|
|
8
16
|
encodeLitmusAttestation,
|
|
17
|
+
encodeSkillAttestation,
|
|
18
|
+
encodeSkillAttestationFields,
|
|
9
19
|
handleRunLitmus,
|
|
20
|
+
handleRunSkillLitmus,
|
|
21
|
+
handleVerifySkill,
|
|
10
22
|
litmusFields,
|
|
11
23
|
litmusSchemaUID,
|
|
12
24
|
networkConfig,
|
|
13
25
|
readAttestation,
|
|
26
|
+
readSkillAttestation,
|
|
14
27
|
rpcUrl,
|
|
15
28
|
runLitmusInputShape,
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
runSkillLitmusInputShape,
|
|
30
|
+
selectedNetwork,
|
|
31
|
+
skillAttestationFields,
|
|
32
|
+
skillSchemaUID,
|
|
33
|
+
verifySkillInputShape
|
|
34
|
+
} from "./chunk-Z66GKAQD.js";
|
|
18
35
|
import {
|
|
19
36
|
parseAuthFlags,
|
|
20
37
|
resolveTarget
|
|
21
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-BUKDFSDO.js";
|
|
22
39
|
import {
|
|
40
|
+
SKILL_BUNDLE_SCHEMA_VERSION,
|
|
41
|
+
SKILL_METHODOLOGY_VERSION,
|
|
42
|
+
SKILL_QUALITY_VERSION,
|
|
43
|
+
SkillLoadError,
|
|
23
44
|
assembleBundle,
|
|
24
45
|
canaryMatch,
|
|
25
46
|
classifyTool,
|
|
26
47
|
connectTarget,
|
|
48
|
+
dangerousCommand,
|
|
49
|
+
exfilInstruction,
|
|
27
50
|
fingerprintToolDefs,
|
|
28
51
|
gradeFromCategories,
|
|
52
|
+
gradeSkillCategories,
|
|
29
53
|
hasHighSeverity,
|
|
30
54
|
instructionMimicry,
|
|
31
55
|
internalsLeak,
|
|
32
56
|
invisibleUnicode,
|
|
57
|
+
judgeFromEnv,
|
|
58
|
+
judgeSkillQuality,
|
|
59
|
+
loadSkill,
|
|
33
60
|
markdownTricks,
|
|
61
|
+
openAICompatJudge,
|
|
62
|
+
overBroadTrigger,
|
|
34
63
|
runLitmus,
|
|
35
|
-
|
|
36
|
-
|
|
64
|
+
runSkillLitmus,
|
|
65
|
+
runSkillQuality,
|
|
66
|
+
runSkillQualityJudged,
|
|
67
|
+
skillInjection,
|
|
68
|
+
skillInjectionFails,
|
|
69
|
+
stateChangingToolNames,
|
|
70
|
+
stripExamples
|
|
71
|
+
} from "./chunk-RYJXVMCT.js";
|
|
37
72
|
import {
|
|
38
73
|
BUNDLE_SCHEMA_VERSION,
|
|
39
74
|
CATEGORY_STATUS_UINT8,
|
|
40
75
|
METHODOLOGY_VERSION,
|
|
41
76
|
ServerRefParseError,
|
|
77
|
+
SkillRefParseError,
|
|
42
78
|
canonicalStringify,
|
|
43
79
|
formatServerRef,
|
|
80
|
+
formatSkillRef,
|
|
44
81
|
parseServerRef,
|
|
45
|
-
|
|
46
|
-
|
|
82
|
+
parseSkillRef,
|
|
83
|
+
serverKey,
|
|
84
|
+
skillKey
|
|
85
|
+
} from "./chunk-44R4ZYOE.js";
|
|
47
86
|
|
|
48
87
|
// ../agent/src/gate.ts
|
|
49
88
|
function sameServer(a, b) {
|
|
@@ -92,41 +131,80 @@ export {
|
|
|
92
131
|
CATEGORY_STATUS_UINT8,
|
|
93
132
|
DEFAULT_PASSING,
|
|
94
133
|
LITMUS_SCHEMA,
|
|
134
|
+
LITMUS_SKILL_SCHEMA,
|
|
95
135
|
METHODOLOGY_VERSION,
|
|
96
136
|
NETWORKS,
|
|
97
137
|
RUN_LITMUS_TOOL_DESCRIPTION,
|
|
98
138
|
RUN_LITMUS_TOOL_NAME,
|
|
99
139
|
RUN_LITMUS_TOOL_TITLE,
|
|
140
|
+
RUN_SKILL_LITMUS_TOOL_DESCRIPTION,
|
|
141
|
+
RUN_SKILL_LITMUS_TOOL_NAME,
|
|
142
|
+
RUN_SKILL_LITMUS_TOOL_TITLE,
|
|
143
|
+
SKILL_BUNDLE_SCHEMA_VERSION,
|
|
144
|
+
SKILL_METHODOLOGY_VERSION,
|
|
145
|
+
SKILL_QUALITY_VERSION,
|
|
100
146
|
ServerRefParseError,
|
|
147
|
+
SkillLoadError,
|
|
148
|
+
SkillRefParseError,
|
|
149
|
+
VERIFY_SKILL_TOOL_DESCRIPTION,
|
|
150
|
+
VERIFY_SKILL_TOOL_NAME,
|
|
151
|
+
VERIFY_SKILL_TOOL_TITLE,
|
|
101
152
|
assembleBundle,
|
|
102
153
|
canaryMatch,
|
|
103
154
|
canonicalStringify,
|
|
104
155
|
classifyTool,
|
|
105
156
|
connectTarget,
|
|
157
|
+
dangerousCommand,
|
|
106
158
|
decodeLitmusAttestation,
|
|
159
|
+
decodeSkillAttestation,
|
|
107
160
|
encodeLitmusAttestation,
|
|
161
|
+
encodeSkillAttestation,
|
|
162
|
+
encodeSkillAttestationFields,
|
|
163
|
+
exfilInstruction,
|
|
108
164
|
fingerprintToolDefs,
|
|
109
165
|
formatServerRef,
|
|
166
|
+
formatSkillRef,
|
|
110
167
|
gateDecision,
|
|
111
168
|
gradeFromCategories,
|
|
169
|
+
gradeSkillCategories,
|
|
112
170
|
handleRunLitmus,
|
|
171
|
+
handleRunSkillLitmus,
|
|
172
|
+
handleVerifySkill,
|
|
113
173
|
hasHighSeverity,
|
|
114
174
|
instructionMimicry,
|
|
115
175
|
internalsLeak,
|
|
116
176
|
invisibleUnicode,
|
|
177
|
+
judgeFromEnv,
|
|
178
|
+
judgeSkillQuality,
|
|
117
179
|
litmusFields,
|
|
118
180
|
litmusSchemaUID,
|
|
119
181
|
liveFingerprint,
|
|
182
|
+
loadSkill,
|
|
120
183
|
markdownTricks,
|
|
121
184
|
networkConfig,
|
|
185
|
+
openAICompatJudge,
|
|
186
|
+
overBroadTrigger,
|
|
122
187
|
parseAuthFlags,
|
|
123
188
|
parseServerRef,
|
|
189
|
+
parseSkillRef,
|
|
124
190
|
readAttestation,
|
|
191
|
+
readSkillAttestation,
|
|
125
192
|
resolveTarget,
|
|
126
193
|
rpcUrl,
|
|
127
194
|
runLitmus,
|
|
128
195
|
runLitmusInputShape,
|
|
196
|
+
runSkillLitmus,
|
|
197
|
+
runSkillLitmusInputShape,
|
|
198
|
+
runSkillQuality,
|
|
199
|
+
runSkillQualityJudged,
|
|
129
200
|
selectedNetwork,
|
|
130
201
|
serverKey,
|
|
131
|
-
|
|
202
|
+
skillAttestationFields,
|
|
203
|
+
skillInjection,
|
|
204
|
+
skillInjectionFails,
|
|
205
|
+
skillKey,
|
|
206
|
+
skillSchemaUID,
|
|
207
|
+
stateChangingToolNames,
|
|
208
|
+
stripExamples,
|
|
209
|
+
verifySkillInputShape
|
|
132
210
|
};
|
package/dist/mcp.js
CHANGED
|
@@ -3,141 +3,61 @@ import {
|
|
|
3
3
|
RUN_LITMUS_TOOL_DESCRIPTION,
|
|
4
4
|
RUN_LITMUS_TOOL_NAME,
|
|
5
5
|
RUN_LITMUS_TOOL_TITLE,
|
|
6
|
+
RUN_SKILL_LITMUS_TOOL_DESCRIPTION,
|
|
7
|
+
RUN_SKILL_LITMUS_TOOL_NAME,
|
|
8
|
+
RUN_SKILL_LITMUS_TOOL_TITLE,
|
|
9
|
+
VERIFY_SKILL_TOOL_DESCRIPTION,
|
|
10
|
+
VERIFY_SKILL_TOOL_NAME,
|
|
11
|
+
VERIFY_SKILL_TOOL_TITLE,
|
|
12
|
+
VERIFY_TOOL_DESCRIPTION,
|
|
13
|
+
VERIFY_TOOL_NAME,
|
|
14
|
+
VERIFY_TOOL_TITLE,
|
|
6
15
|
handleRunLitmus,
|
|
7
|
-
|
|
16
|
+
handleRunSkillLitmus,
|
|
17
|
+
handleVerify,
|
|
18
|
+
handleVerifySkill,
|
|
8
19
|
runLitmusInputShape,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
runSkillLitmusInputShape,
|
|
21
|
+
verifyInputShape,
|
|
22
|
+
verifySkillInputShape
|
|
23
|
+
} from "./chunk-Z66GKAQD.js";
|
|
24
|
+
import "./chunk-BUKDFSDO.js";
|
|
13
25
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
26
|
+
judgeFromEnv
|
|
27
|
+
} from "./chunk-RYJXVMCT.js";
|
|
28
|
+
import "./chunk-44R4ZYOE.js";
|
|
17
29
|
|
|
18
30
|
// src/mcp.ts
|
|
19
31
|
import { realpathSync } from "fs";
|
|
20
32
|
import { fileURLToPath } from "url";
|
|
21
|
-
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
22
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
23
|
-
import { z as z2 } from "zod";
|
|
24
|
-
|
|
25
|
-
// ../mcp/src/index.ts
|
|
26
33
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
27
|
-
|
|
28
|
-
// ../mcp/src/tools/verify-attestation.ts
|
|
34
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
29
35
|
import { z } from "zod";
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return ref.trim().toLowerCase();
|
|
35
|
-
}
|
|
36
|
+
|
|
37
|
+
// src/sampling-judge.ts
|
|
38
|
+
function clientSupportsSampling(server) {
|
|
39
|
+
return Boolean(server.server.getClientCapabilities()?.sampling);
|
|
36
40
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"today: that means UNEVALUATED (neither safe nor unsafe), not a failing grade \u2014 to",
|
|
51
|
-
"grade the server yourself right now, use `run_litmus`. A `lookup_failed` result",
|
|
52
|
-
"means the lookup itself failed (the index or chain was unreachable); the grade is",
|
|
53
|
-
"unknown, which is not the same as unevaluated.",
|
|
54
|
-
"",
|
|
55
|
-
"Input: server_ref \u2014 e.g. npm/@modelcontextprotocol/server-filesystem."
|
|
56
|
-
].join("\n");
|
|
57
|
-
var verifyInputShape = {
|
|
58
|
-
server_ref: z.string().min(1).max(512).describe("Registry-prefixed server identifier, e.g. npm/@scope/server.")
|
|
59
|
-
};
|
|
60
|
-
async function handleVerify({ server_ref }) {
|
|
61
|
-
const found = await resolveUid(server_ref);
|
|
62
|
-
if (found.kind === "error") {
|
|
63
|
-
return {
|
|
64
|
-
isError: true,
|
|
65
|
-
content: [
|
|
66
|
-
{
|
|
67
|
-
type: "text",
|
|
68
|
-
text: `lookup_failed \u2014 could not reach the polygraph grade index for ${server_ref} (${found.detail}). The lookup itself failed, so the grade is unknown \u2014 retry or report it as unchecked, NOT as unevaluated.`
|
|
69
|
-
}
|
|
70
|
-
]
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
let att = null;
|
|
74
|
-
if (found.kind === "found") {
|
|
75
|
-
try {
|
|
76
|
-
att = await readAttestation(found.uid);
|
|
77
|
-
} catch (err) {
|
|
78
|
-
return {
|
|
79
|
-
isError: true,
|
|
80
|
-
content: [
|
|
81
|
-
{
|
|
82
|
-
type: "text",
|
|
83
|
-
text: `lookup_failed \u2014 the onchain read failed for ${server_ref} (${err instanceof Error ? err.message : String(err)}). Treat as unchecked (the chain/RPC was unreachable), not as "no grade".`
|
|
84
|
-
}
|
|
85
|
-
]
|
|
86
|
-
};
|
|
41
|
+
function samplingJudge(server) {
|
|
42
|
+
return {
|
|
43
|
+
id: "mcp-sampling",
|
|
44
|
+
async complete(system, user) {
|
|
45
|
+
if (!clientSupportsSampling(server)) {
|
|
46
|
+
throw new Error("MCP client does not support sampling");
|
|
47
|
+
}
|
|
48
|
+
const res = await server.server.createMessage({
|
|
49
|
+
systemPrompt: system,
|
|
50
|
+
maxTokens: 1024,
|
|
51
|
+
messages: [{ role: "user", content: { type: "text", text: user } }]
|
|
52
|
+
});
|
|
53
|
+
return res.content.type === "text" ? res.content.text : "";
|
|
87
54
|
}
|
|
88
|
-
}
|
|
89
|
-
if (!att) {
|
|
90
|
-
return {
|
|
91
|
-
content: [
|
|
92
|
-
{
|
|
93
|
-
type: "text",
|
|
94
|
-
text: `not_available \u2014 no published polygraph grade for ${server_ref}. Grade publishing is still rolling out, so this is expected for most servers; it means unevaluated (neither safe nor unsafe), not a failing grade. To grade it now, use run_litmus.`
|
|
95
|
-
}
|
|
96
|
-
]
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
if (canonicalRef(att.serverRef) !== canonicalRef(server_ref)) {
|
|
100
|
-
return {
|
|
101
|
-
content: [
|
|
102
|
-
{
|
|
103
|
-
type: "text",
|
|
104
|
-
text: `not_available \u2014 the resolved attestation is for ${att.serverRef}, not ${server_ref} (discovery mismatch; treat as unevaluated)`
|
|
105
|
-
}
|
|
106
|
-
]
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
const payload = {
|
|
110
|
-
status: "attested",
|
|
111
|
-
grade: att.overallGrade,
|
|
112
|
-
attestationUid: att.uid,
|
|
113
|
-
serverRef: att.serverRef,
|
|
114
|
-
// The version the grade was run against (null for HTTP/unresolved targets).
|
|
115
|
-
// Advisory: the live fingerprint, not this string, is the trust anchor.
|
|
116
|
-
resolvedVersion: att.resolvedVersion,
|
|
117
|
-
reportCID: att.reportCID,
|
|
118
|
-
toolDefsFingerprint: att.toolDefsFingerprint,
|
|
119
|
-
revoked: att.revoked,
|
|
120
|
-
network: selectedNetwork(),
|
|
121
|
-
liveFingerprintCheckRequired: "Recompute the live tool-surface fingerprint and require it to equal toolDefsFingerprint before paying."
|
|
122
55
|
};
|
|
123
|
-
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
124
|
-
}
|
|
125
|
-
async function resolveUid(serverRef) {
|
|
126
|
-
const base = process.env.POLYGRAPH_API_URL ?? "https://polygraph.so";
|
|
127
|
-
try {
|
|
128
|
-
const res = await fetch(`${base}/api/attestations?ref=${encodeURIComponent(serverRef)}`);
|
|
129
|
-
if (res.status === 404) return { kind: "none" };
|
|
130
|
-
if (!res.ok) return { kind: "error", detail: `grade index returned HTTP ${res.status}` };
|
|
131
|
-
const row = await res.json();
|
|
132
|
-
return row?.attestation_uid ? { kind: "found", uid: row.attestation_uid } : { kind: "none" };
|
|
133
|
-
} catch (err) {
|
|
134
|
-
return { kind: "error", detail: err instanceof Error ? err.message : String(err) };
|
|
135
|
-
}
|
|
136
56
|
}
|
|
137
57
|
|
|
138
58
|
// src/mcp.ts
|
|
139
59
|
function buildServer() {
|
|
140
|
-
const server = new
|
|
60
|
+
const server = new McpServer(
|
|
141
61
|
{ name: "polygraph-litmus", version: "0.1.0" },
|
|
142
62
|
{
|
|
143
63
|
instructions: [
|
|
@@ -155,7 +75,12 @@ function buildServer() {
|
|
|
155
75
|
"server, without running anything. Grade publishing is still rolling out, so",
|
|
156
76
|
"it commonly returns not_available today \u2014 that means unevaluated (neither",
|
|
157
77
|
"safe nor unsafe), not a failing grade; to grade the server yourself, use",
|
|
158
|
-
"`run_litmus`."
|
|
78
|
+
"`run_litmus`.",
|
|
79
|
+
"",
|
|
80
|
+
"Use `run_skill_litmus` to grade a Claude Code / Agent Skill (a SKILL.md +",
|
|
81
|
+
"bundle) A/B/D/F. This is a STATIC read of the skill's text and bundled files \u2014",
|
|
82
|
+
"no execution, no network \u2014 so it is fast but not behavioral proof. Pass",
|
|
83
|
+
"`skill_ref` as a local path to the skill directory."
|
|
159
84
|
].join("\n")
|
|
160
85
|
}
|
|
161
86
|
);
|
|
@@ -179,6 +104,30 @@ function buildServer() {
|
|
|
179
104
|
},
|
|
180
105
|
handleRunLitmus
|
|
181
106
|
);
|
|
107
|
+
server.registerTool(
|
|
108
|
+
RUN_SKILL_LITMUS_TOOL_NAME,
|
|
109
|
+
{
|
|
110
|
+
title: RUN_SKILL_LITMUS_TOOL_TITLE,
|
|
111
|
+
description: RUN_SKILL_LITMUS_TOOL_DESCRIPTION,
|
|
112
|
+
inputSchema: runSkillLitmusInputShape,
|
|
113
|
+
annotations: {
|
|
114
|
+
title: RUN_SKILL_LITMUS_TOOL_TITLE,
|
|
115
|
+
readOnlyHint: true,
|
|
116
|
+
// never mutates: the safety scan reads files; quality judging is host-mediated
|
|
117
|
+
destructiveHint: false,
|
|
118
|
+
idempotentHint: false,
|
|
119
|
+
// the optional LLM-judged quality axes are non-deterministic
|
|
120
|
+
openWorldHint: true
|
|
121
|
+
// the optional quality judge may use the host model (sampling) or a configured endpoint
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
// Resolve the judge per call (the client connection is known now): the host
|
|
125
|
+
// agent's model via sampling if it's offered, else an operator-set env key,
|
|
126
|
+
// else null ⇒ deterministic quality only. The litmus core never needs a key.
|
|
127
|
+
(args) => handleRunSkillLitmus(args, {
|
|
128
|
+
judge: clientSupportsSampling(server) ? samplingJudge(server) : judgeFromEnv()
|
|
129
|
+
})
|
|
130
|
+
);
|
|
182
131
|
server.registerTool(
|
|
183
132
|
VERIFY_TOOL_NAME,
|
|
184
133
|
{
|
|
@@ -195,13 +144,30 @@ function buildServer() {
|
|
|
195
144
|
},
|
|
196
145
|
handleVerify
|
|
197
146
|
);
|
|
147
|
+
server.registerTool(
|
|
148
|
+
VERIFY_SKILL_TOOL_NAME,
|
|
149
|
+
{
|
|
150
|
+
title: VERIFY_SKILL_TOOL_TITLE,
|
|
151
|
+
description: VERIFY_SKILL_TOOL_DESCRIPTION,
|
|
152
|
+
inputSchema: verifySkillInputShape,
|
|
153
|
+
annotations: {
|
|
154
|
+
title: VERIFY_SKILL_TOOL_TITLE,
|
|
155
|
+
readOnlyHint: true,
|
|
156
|
+
destructiveHint: false,
|
|
157
|
+
idempotentHint: true,
|
|
158
|
+
openWorldHint: true
|
|
159
|
+
// reads the grade index + chain
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
handleVerifySkill
|
|
163
|
+
);
|
|
198
164
|
server.registerPrompt(
|
|
199
165
|
"grade",
|
|
200
166
|
{
|
|
201
167
|
title: "Grade an MCP server",
|
|
202
168
|
description: "Run the open behavioral litmus against an MCP server and report its grade A\u2013F with the evidence.",
|
|
203
169
|
argsSchema: {
|
|
204
|
-
server_ref:
|
|
170
|
+
server_ref: z.string().min(1).max(512).describe("npm/@scope/server, an https:// MCP URL, or a local path to an MCP entry file")
|
|
205
171
|
}
|
|
206
172
|
},
|
|
207
173
|
({ server_ref }) => ({
|
|
@@ -222,7 +188,7 @@ function buildServer() {
|
|
|
222
188
|
title: "Check a server's published grade",
|
|
223
189
|
description: "Read a server's already-published polygraph grade without running anything.",
|
|
224
190
|
argsSchema: {
|
|
225
|
-
server_ref:
|
|
191
|
+
server_ref: z.string().min(1).max(512).describe("Registry-prefixed server identifier, e.g. npm/@scope/server")
|
|
226
192
|
}
|
|
227
193
|
},
|
|
228
194
|
({ server_ref }) => ({
|
|
@@ -237,6 +203,48 @@ function buildServer() {
|
|
|
237
203
|
]
|
|
238
204
|
})
|
|
239
205
|
);
|
|
206
|
+
server.registerPrompt(
|
|
207
|
+
"grade-skill",
|
|
208
|
+
{
|
|
209
|
+
title: "Grade a Claude Code skill",
|
|
210
|
+
description: "Run the open static safety litmus over a skill (SKILL.md + bundle) and report its grade A/B/D/F with the evidence.",
|
|
211
|
+
argsSchema: {
|
|
212
|
+
skill_ref: z.string().min(1).max(1024).describe("Local path to a skill directory containing SKILL.md")
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
({ skill_ref }) => ({
|
|
216
|
+
messages: [
|
|
217
|
+
{
|
|
218
|
+
role: "user",
|
|
219
|
+
content: {
|
|
220
|
+
type: "text",
|
|
221
|
+
text: `Run the polygraph skill litmus on ${skill_ref} using the run_skill_litmus tool. Report the letter grade, the one-line summary, any failed category with its findings, and the contentHash. State plainly that this is a static scan, not behavioral proof.`
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
server.registerPrompt(
|
|
228
|
+
"check-skill",
|
|
229
|
+
{
|
|
230
|
+
title: "Check a skill's published grade",
|
|
231
|
+
description: "Read a skill's already-published polygraph grade without running anything.",
|
|
232
|
+
argsSchema: {
|
|
233
|
+
skill_ref: z.string().min(1).max(1024).describe("Skill identifier, e.g. github/<owner>/<repo>#<path> or marketplace/<owner>/<name>")
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
({ skill_ref }) => ({
|
|
237
|
+
messages: [
|
|
238
|
+
{
|
|
239
|
+
role: "user",
|
|
240
|
+
content: {
|
|
241
|
+
type: "text",
|
|
242
|
+
text: `Use the verify_skill_attestation tool to read the published polygraph grade for ${skill_ref}. If it returns not_available, say the skill is unevaluated (neither safe nor unsafe) and offer to grade a local copy with run_skill_litmus. If a grade is returned, report it and remind the user to recompute the skill's contentHash before installing.`
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
})
|
|
247
|
+
);
|
|
240
248
|
return server;
|
|
241
249
|
}
|
|
242
250
|
async function main() {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SKILL_BUNDLE_SCHEMA_VERSION,
|
|
3
|
+
SKILL_METHODOLOGY_VERSION,
|
|
4
|
+
SKILL_QUALITY_VERSION,
|
|
5
|
+
SkillLoadError,
|
|
6
|
+
assembleBundle,
|
|
7
|
+
canaryMatch,
|
|
8
|
+
classifyTool,
|
|
9
|
+
connectTarget,
|
|
10
|
+
dangerousCommand,
|
|
11
|
+
exfilInstruction,
|
|
12
|
+
fingerprintToolDefs,
|
|
13
|
+
gradeFromCategories,
|
|
14
|
+
gradeSkillCategories,
|
|
15
|
+
hasHighSeverity,
|
|
16
|
+
instructionMimicry,
|
|
17
|
+
internalsLeak,
|
|
18
|
+
invisibleUnicode,
|
|
19
|
+
judgeFromEnv,
|
|
20
|
+
judgeSkillQuality,
|
|
21
|
+
loadSkill,
|
|
22
|
+
markdownTricks,
|
|
23
|
+
openAICompatJudge,
|
|
24
|
+
overBroadTrigger,
|
|
25
|
+
runLitmus,
|
|
26
|
+
runSkillLitmus,
|
|
27
|
+
runSkillQuality,
|
|
28
|
+
runSkillQualityJudged,
|
|
29
|
+
skillInjection,
|
|
30
|
+
skillInjectionFails,
|
|
31
|
+
stateChangingToolNames,
|
|
32
|
+
stripExamples
|
|
33
|
+
} from "./chunk-RYJXVMCT.js";
|
|
34
|
+
import "./chunk-44R4ZYOE.js";
|
|
35
|
+
export {
|
|
36
|
+
SKILL_BUNDLE_SCHEMA_VERSION,
|
|
37
|
+
SKILL_METHODOLOGY_VERSION,
|
|
38
|
+
SKILL_QUALITY_VERSION,
|
|
39
|
+
SkillLoadError,
|
|
40
|
+
assembleBundle,
|
|
41
|
+
canaryMatch,
|
|
42
|
+
classifyTool,
|
|
43
|
+
connectTarget,
|
|
44
|
+
dangerousCommand,
|
|
45
|
+
exfilInstruction,
|
|
46
|
+
fingerprintToolDefs,
|
|
47
|
+
gradeFromCategories,
|
|
48
|
+
gradeSkillCategories,
|
|
49
|
+
hasHighSeverity,
|
|
50
|
+
instructionMimicry,
|
|
51
|
+
internalsLeak,
|
|
52
|
+
invisibleUnicode,
|
|
53
|
+
judgeFromEnv,
|
|
54
|
+
judgeSkillQuality,
|
|
55
|
+
loadSkill,
|
|
56
|
+
markdownTricks,
|
|
57
|
+
openAICompatJudge,
|
|
58
|
+
overBroadTrigger,
|
|
59
|
+
runLitmus,
|
|
60
|
+
runSkillLitmus,
|
|
61
|
+
runSkillQuality,
|
|
62
|
+
runSkillQualityJudged,
|
|
63
|
+
skillInjection,
|
|
64
|
+
skillInjectionFails,
|
|
65
|
+
stateChangingToolNames,
|
|
66
|
+
stripExamples
|
|
67
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polygraphso/litmus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Behavioral litmus harness for MCP servers — grade a server A–F (tool-output injection, egress, sensitive-data, adversarial-input) with reproducible, content-addressed evidence. Ships a CLI and an MCP server with a run_litmus tool for AI agents.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://polygraph.so",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"bin": {
|
|
42
42
|
"polygraphso-litmus": "dist/cli.js",
|
|
43
|
+
"polygraphso-litmus-skill": "dist/cli-skill.js",
|
|
43
44
|
"polygraphso-litmus-mcp": "dist/mcp.js"
|
|
44
45
|
},
|
|
45
46
|
"files": [
|
|
@@ -65,8 +66,8 @@
|
|
|
65
66
|
"@polygraph/onchain": "0.0.0",
|
|
66
67
|
"@polygraph/agent": "0.0.0",
|
|
67
68
|
"@polygraph/probes": "0.0.0",
|
|
68
|
-
"@polygraph/
|
|
69
|
-
"@polygraph/
|
|
69
|
+
"@polygraph/cli": "0.0.0",
|
|
70
|
+
"@polygraph/mcp": "0.0.0"
|
|
70
71
|
},
|
|
71
72
|
"publishConfig": {
|
|
72
73
|
"access": "public"
|