@atbash/atbash-mcp 0.0.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/LICENSE +14 -0
- package/README.md +161 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +449 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Atbash CLI — Proprietary Software License
|
|
2
|
+
|
|
3
|
+
Copyright © 2026 Atbash. All rights reserved.
|
|
4
|
+
|
|
5
|
+
The full license is available at https://atbash.ai/license
|
|
6
|
+
|
|
7
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
10
|
+
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
11
|
+
|
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
13
|
+
|
|
14
|
+
The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of Atbash.
|
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# `@atbash/mcp`
|
|
2
|
+
|
|
3
|
+
Expose Atbash as a standalone MCP server.
|
|
4
|
+
|
|
5
|
+
This package starts an MCP server process that loads one Atbash agent identity and exposes safety and query tools over the Model Context Protocol. Your MCP host or client remains the real decision-maker — this package does not execute your business tools.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @atbash/mcp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## When To Use It
|
|
14
|
+
|
|
15
|
+
Use this package when:
|
|
16
|
+
|
|
17
|
+
- your host already supports MCP
|
|
18
|
+
- you want one Atbash tool server to serve one or many MCP clients
|
|
19
|
+
- you want Atbash checks without coupling your app directly to the SDK
|
|
20
|
+
|
|
21
|
+
Do not use this when you need deep framework lifecycle integration — use a framework-native package like `@atbash/eliza-plugin` or `@atbash/langgraph` instead.
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### Claude Desktop
|
|
26
|
+
|
|
27
|
+
Add to your `claude_desktop_config.json`:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"atbash": {
|
|
33
|
+
"command": "npx",
|
|
34
|
+
"args": ["-y", "@atbash/mcp"],
|
|
35
|
+
"env": {
|
|
36
|
+
"ATBASH_AGENT_PRIVKEY": "your_agent_private_key_here"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Any MCP Client
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
47
|
+
|
|
48
|
+
const transport = new StdioClientTransport({
|
|
49
|
+
command: "npx",
|
|
50
|
+
args: ["-y", "@atbash/mcp"],
|
|
51
|
+
env: {
|
|
52
|
+
...process.env,
|
|
53
|
+
ATBASH_AGENT_PRIVKEY: process.env.ATBASH_AGENT_PRIVKEY,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Environment Variables
|
|
59
|
+
|
|
60
|
+
| Variable | Required | Description |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `ATBASH_AGENT_PRIVKEY` | Yes | Your Atbash agent private key |
|
|
63
|
+
| `ATBASH_ENDPOINT` | No | Override the default Atbash endpoint (`https://atbash.ai`) |
|
|
64
|
+
|
|
65
|
+
## Tools
|
|
66
|
+
|
|
67
|
+
### Safety Tools
|
|
68
|
+
|
|
69
|
+
#### `atbash_judge`
|
|
70
|
+
|
|
71
|
+
Submit an action for safety judgment before executing it.
|
|
72
|
+
|
|
73
|
+
| Input | Type | Required | Description |
|
|
74
|
+
|---|---|---|---|
|
|
75
|
+
| `action` | string | Yes | Plain text description of the action |
|
|
76
|
+
| `context` | string | Yes | Why this action is being taken |
|
|
77
|
+
| `tool_name` | string | No | Name of the tool being called |
|
|
78
|
+
| `tool_args_json` | string | No | JSON string of tool arguments |
|
|
79
|
+
|
|
80
|
+
Returns: `{ verdict, allow, reason, tool_call_id }`
|
|
81
|
+
|
|
82
|
+
#### `atbash_log`
|
|
83
|
+
|
|
84
|
+
Log a tool call on-chain without requesting a verdict.
|
|
85
|
+
|
|
86
|
+
| Input | Type | Required |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `action` | string | Yes |
|
|
89
|
+
| `context` | string | Yes |
|
|
90
|
+
| `tool_name` | string | No |
|
|
91
|
+
| `tool_args_json` | string | No |
|
|
92
|
+
|
|
93
|
+
#### `atbash_check_agent`
|
|
94
|
+
|
|
95
|
+
Check whether an agent is registered on the Atbash platform.
|
|
96
|
+
|
|
97
|
+
| Input | Type | Description |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| `pubkey` | string | Agent public key (defaults to server agent) |
|
|
100
|
+
|
|
101
|
+
#### `atbash_judgment_status`
|
|
102
|
+
|
|
103
|
+
Poll the status of a previously submitted judgment.
|
|
104
|
+
|
|
105
|
+
| Input | Type | Description |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `tool_call_id` | string | ID returned from `atbash_judge` |
|
|
108
|
+
| `agent_pubkey` | string | Agent public key (defaults to server agent) |
|
|
109
|
+
|
|
110
|
+
### Query Tools (read-only)
|
|
111
|
+
|
|
112
|
+
| Tool | Description |
|
|
113
|
+
|---|---|
|
|
114
|
+
| `atbash_get_policy` | Get agent policy and jail status |
|
|
115
|
+
| `atbash_get_agent_detail` | Get agent metadata |
|
|
116
|
+
| `atbash_get_tool_calls` | List recent tool calls across all agents |
|
|
117
|
+
| `atbash_get_agent_tool_calls` | List recent tool calls for one agent |
|
|
118
|
+
| `atbash_get_org_tool_calls` | List recent tool calls for an organization |
|
|
119
|
+
| `atbash_get_tool_call_full` | Get full detail for a single tool call |
|
|
120
|
+
| `atbash_get_tool_call_count` | Get total on-chain tool call count |
|
|
121
|
+
| `atbash_get_tier_info` | Get organization tier information |
|
|
122
|
+
| `atbash_get_held_actions` | List actions pending operator review |
|
|
123
|
+
| `atbash_get_reviews` | List completed operator reviews |
|
|
124
|
+
| `atbash_get_safety_stats` | Get chain-wide safety statistics |
|
|
125
|
+
|
|
126
|
+
## Verdict Handling
|
|
127
|
+
|
|
128
|
+
| Verdict | Meaning | What To Do |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| `ALLOW` | Safe to proceed | Execute the real tool |
|
|
131
|
+
| `HOLD` | Logged for async review | Execution is allowed; the action is flagged for operator review in the background — keep `tool_call_id` to poll status later |
|
|
132
|
+
| `BLOCK` | Policy violation | Stop; show `reason` to operator |
|
|
133
|
+
| `ERROR` | Judge unreachable | Fail closed |
|
|
134
|
+
|
|
135
|
+
## Sending Better Inputs
|
|
136
|
+
|
|
137
|
+
Better inputs produce better safety decisions.
|
|
138
|
+
|
|
139
|
+
Weak:
|
|
140
|
+
```json
|
|
141
|
+
{ "action": "do payment", "context": "finance" }
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Better:
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"action": "Bank transfer $25 to a new external vendor account",
|
|
148
|
+
"context": "Treasury payout review before execution",
|
|
149
|
+
"tool_name": "send_bank_transfer",
|
|
150
|
+
"tool_args_json": "{\"amount\":25,\"recipient\":\"new vendor\"}"
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Example
|
|
155
|
+
|
|
156
|
+
A runnable example is in [`examples/mcp-runtime-agent/`](./examples/mcp-runtime-agent/).
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npm install && npm run build
|
|
160
|
+
ATBASH_AGENT_PRIVKEY=your_key_here node examples/mcp-runtime-agent/client.mjs
|
|
161
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { loadAgent } from "@atbash/sdk";
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
|
|
8
|
+
// src/prompts/safety.ts
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
function registerSafetyPrompt(server2) {
|
|
11
|
+
const promptServer = server2;
|
|
12
|
+
if (!promptServer.registerPrompt) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
promptServer.registerPrompt(
|
|
16
|
+
"atbash_safety_check",
|
|
17
|
+
{
|
|
18
|
+
description: "Prepare a consistent pre-execution Atbash safety review request.",
|
|
19
|
+
inputSchema: z.object({
|
|
20
|
+
action: z.string().describe("Action to evaluate"),
|
|
21
|
+
context: z.string().describe("Why the action is being taken")
|
|
22
|
+
})
|
|
23
|
+
},
|
|
24
|
+
async ({ action, context }) => ({
|
|
25
|
+
messages: [
|
|
26
|
+
{
|
|
27
|
+
role: "user",
|
|
28
|
+
content: {
|
|
29
|
+
type: "text",
|
|
30
|
+
text: [
|
|
31
|
+
"Use the Atbash tools to evaluate the following action before execution.",
|
|
32
|
+
`Action: ${action}`,
|
|
33
|
+
`Context: ${context}`,
|
|
34
|
+
"If the result is HOLD, explain that operator review is required before proceeding."
|
|
35
|
+
].join("\n")
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/resources/policy.ts
|
|
44
|
+
function registerPolicyResource(server2, agent2, endpoint2) {
|
|
45
|
+
void server2;
|
|
46
|
+
void agent2;
|
|
47
|
+
void endpoint2;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/tools/judge.ts
|
|
52
|
+
import {
|
|
53
|
+
checkAgentExists,
|
|
54
|
+
createAtbashClient,
|
|
55
|
+
logToolCall
|
|
56
|
+
} from "@atbash/sdk";
|
|
57
|
+
import { z as z2 } from "zod";
|
|
58
|
+
|
|
59
|
+
// src/utils.ts
|
|
60
|
+
function createClientOpts(endpoint2) {
|
|
61
|
+
return endpoint2 ? { endpoint: endpoint2 } : void 0;
|
|
62
|
+
}
|
|
63
|
+
function safeErrorMessage(error) {
|
|
64
|
+
return error instanceof Error ? error.message : String(error);
|
|
65
|
+
}
|
|
66
|
+
function toJsonContent(value) {
|
|
67
|
+
return {
|
|
68
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function toErrorContent(error) {
|
|
72
|
+
return {
|
|
73
|
+
content: [{ type: "text", text: `Error: ${safeErrorMessage(error)}` }],
|
|
74
|
+
isError: true
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/tools/judge.ts
|
|
79
|
+
function registerJudgeTools(server2, agent2, endpoint2) {
|
|
80
|
+
const opts = createClientOpts(endpoint2);
|
|
81
|
+
const client = createAtbashClient({
|
|
82
|
+
keyPair: { privKey: agent2.privkey, pubKey: agent2.pubkey },
|
|
83
|
+
judge: endpoint2 ? { endpoint: endpoint2 } : void 0
|
|
84
|
+
});
|
|
85
|
+
server2.registerTool(
|
|
86
|
+
"atbash_judge",
|
|
87
|
+
{
|
|
88
|
+
description: "Submit an action for safety judgment before executing it. Returns ALLOW, HOLD, BLOCK, or ERROR.",
|
|
89
|
+
inputSchema: z2.object({
|
|
90
|
+
action: z2.string().describe("Plain text description of the action to judge"),
|
|
91
|
+
context: z2.string().describe("Why this action is being taken"),
|
|
92
|
+
tool_name: z2.string().optional().describe("Name of the tool being called"),
|
|
93
|
+
tool_args_json: z2.string().optional().describe("JSON string of tool arguments")
|
|
94
|
+
})
|
|
95
|
+
},
|
|
96
|
+
async ({ action, context, tool_name, tool_args_json }) => {
|
|
97
|
+
try {
|
|
98
|
+
let parsedArgs = void 0;
|
|
99
|
+
if (tool_args_json) {
|
|
100
|
+
try {
|
|
101
|
+
parsedArgs = JSON.parse(tool_args_json);
|
|
102
|
+
} catch {
|
|
103
|
+
parsedArgs = tool_args_json;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const decision = await client.auditToolCall({
|
|
107
|
+
toolName: tool_name ?? "mcp_judge",
|
|
108
|
+
args: parsedArgs ?? { action },
|
|
109
|
+
context: `${action} \u2014 ${context}`
|
|
110
|
+
});
|
|
111
|
+
const normalized = decision.verdict === "HOLD" ? { ...decision, verdict: "ALLOW", allow: true } : decision;
|
|
112
|
+
return toJsonContent(normalized);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
return toErrorContent(error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
server2.registerTool(
|
|
119
|
+
"atbash_log",
|
|
120
|
+
{
|
|
121
|
+
description: "Log a tool call on-chain without requesting a verdict.",
|
|
122
|
+
inputSchema: z2.object({
|
|
123
|
+
action: z2.string().describe("Action description"),
|
|
124
|
+
context: z2.string().describe("Action context"),
|
|
125
|
+
tool_name: z2.string().optional().describe("Tool name"),
|
|
126
|
+
tool_args_json: z2.string().optional().describe("Tool arguments JSON")
|
|
127
|
+
})
|
|
128
|
+
},
|
|
129
|
+
async ({ action, context, tool_name, tool_args_json }) => {
|
|
130
|
+
try {
|
|
131
|
+
const result = await logToolCall(
|
|
132
|
+
action,
|
|
133
|
+
context,
|
|
134
|
+
agent2,
|
|
135
|
+
void 0,
|
|
136
|
+
{
|
|
137
|
+
toolName: tool_name,
|
|
138
|
+
toolArgsJson: tool_args_json
|
|
139
|
+
},
|
|
140
|
+
opts
|
|
141
|
+
);
|
|
142
|
+
return toJsonContent(result);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
return toErrorContent(error);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
server2.registerTool(
|
|
149
|
+
"atbash_check_agent",
|
|
150
|
+
{
|
|
151
|
+
description: "Check whether an agent is registered on the Atbash platform.",
|
|
152
|
+
inputSchema: z2.object({
|
|
153
|
+
pubkey: z2.string().optional().describe("Agent public key, defaults to this server agent")
|
|
154
|
+
})
|
|
155
|
+
},
|
|
156
|
+
async ({ pubkey }) => {
|
|
157
|
+
try {
|
|
158
|
+
const selectedPubkey = pubkey ?? agent2.pubkey;
|
|
159
|
+
const registered = await checkAgentExists(selectedPubkey, opts);
|
|
160
|
+
return toJsonContent({ registered, pubkey: selectedPubkey });
|
|
161
|
+
} catch (error) {
|
|
162
|
+
return toErrorContent(error);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/tools/queries.ts
|
|
169
|
+
import {
|
|
170
|
+
getAgentDetail,
|
|
171
|
+
getAgentPolicy,
|
|
172
|
+
getAgentToolCalls,
|
|
173
|
+
getHeldActionReviews,
|
|
174
|
+
getOrgTierInfo,
|
|
175
|
+
getOrgToolCalls,
|
|
176
|
+
getPendingHeldActions,
|
|
177
|
+
getSafetyStats,
|
|
178
|
+
getToolCallCount,
|
|
179
|
+
getToolCallFull,
|
|
180
|
+
getToolCalls
|
|
181
|
+
} from "@atbash/sdk";
|
|
182
|
+
import { z as z3 } from "zod";
|
|
183
|
+
function defaultMaxCount(value) {
|
|
184
|
+
return value ?? 20;
|
|
185
|
+
}
|
|
186
|
+
function registerQueryTools(server2, agent2, endpoint2) {
|
|
187
|
+
const opts = createClientOpts(endpoint2);
|
|
188
|
+
server2.registerTool(
|
|
189
|
+
"atbash_get_policy",
|
|
190
|
+
{
|
|
191
|
+
description: "Get an agent policy configuration and jail status.",
|
|
192
|
+
inputSchema: z3.object({
|
|
193
|
+
pubkey: z3.string().optional().describe("Agent public key, defaults to the server agent")
|
|
194
|
+
})
|
|
195
|
+
},
|
|
196
|
+
async ({ pubkey }) => {
|
|
197
|
+
try {
|
|
198
|
+
return toJsonContent(await getAgentPolicy(pubkey ?? agent2.pubkey, opts));
|
|
199
|
+
} catch (error) {
|
|
200
|
+
return toErrorContent(error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
server2.registerTool(
|
|
205
|
+
"atbash_get_agent_detail",
|
|
206
|
+
{
|
|
207
|
+
description: "Get agent metadata for a public key.",
|
|
208
|
+
inputSchema: z3.object({
|
|
209
|
+
pubkey: z3.string().optional().describe("Agent public key, defaults to the server agent")
|
|
210
|
+
})
|
|
211
|
+
},
|
|
212
|
+
async ({ pubkey }) => {
|
|
213
|
+
try {
|
|
214
|
+
return toJsonContent(await getAgentDetail(pubkey ?? agent2.pubkey, opts));
|
|
215
|
+
} catch (error) {
|
|
216
|
+
return toErrorContent(error);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
server2.registerTool(
|
|
221
|
+
"atbash_get_tool_calls",
|
|
222
|
+
{
|
|
223
|
+
description: "List recent tool calls across all agents.",
|
|
224
|
+
inputSchema: z3.object({
|
|
225
|
+
max_count: z3.number().int().positive().optional().describe("Maximum number of records")
|
|
226
|
+
})
|
|
227
|
+
},
|
|
228
|
+
async ({ max_count }) => {
|
|
229
|
+
try {
|
|
230
|
+
return toJsonContent(await getToolCalls(defaultMaxCount(max_count), opts));
|
|
231
|
+
} catch (error) {
|
|
232
|
+
return toErrorContent(error);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
server2.registerTool(
|
|
237
|
+
"atbash_get_agent_tool_calls",
|
|
238
|
+
{
|
|
239
|
+
description: "List recent tool calls for one agent.",
|
|
240
|
+
inputSchema: z3.object({
|
|
241
|
+
pubkey: z3.string().optional().describe("Agent public key, defaults to the server agent"),
|
|
242
|
+
max_count: z3.number().int().positive().optional().describe("Maximum number of records")
|
|
243
|
+
})
|
|
244
|
+
},
|
|
245
|
+
async ({ pubkey, max_count }) => {
|
|
246
|
+
try {
|
|
247
|
+
return toJsonContent(
|
|
248
|
+
await getAgentToolCalls(pubkey ?? agent2.pubkey, defaultMaxCount(max_count), opts)
|
|
249
|
+
);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
return toErrorContent(error);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
server2.registerTool(
|
|
256
|
+
"atbash_get_org_tool_calls",
|
|
257
|
+
{
|
|
258
|
+
description: "List recent tool calls for an organization.",
|
|
259
|
+
inputSchema: z3.object({
|
|
260
|
+
org_name: z3.string().describe("Organization name"),
|
|
261
|
+
max_count: z3.number().int().positive().optional().describe("Maximum number of records")
|
|
262
|
+
})
|
|
263
|
+
},
|
|
264
|
+
async ({ org_name, max_count }) => {
|
|
265
|
+
try {
|
|
266
|
+
return toJsonContent(await getOrgToolCalls(org_name, defaultMaxCount(max_count), opts));
|
|
267
|
+
} catch (error) {
|
|
268
|
+
return toErrorContent(error);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
server2.registerTool(
|
|
273
|
+
"atbash_get_tool_call_full",
|
|
274
|
+
{
|
|
275
|
+
description: "Get the full detail for a single tool call.",
|
|
276
|
+
inputSchema: z3.object({
|
|
277
|
+
tool_call_id: z3.string().describe("Tool call identifier")
|
|
278
|
+
})
|
|
279
|
+
},
|
|
280
|
+
async ({ tool_call_id }) => {
|
|
281
|
+
try {
|
|
282
|
+
return toJsonContent(await getToolCallFull(tool_call_id, opts));
|
|
283
|
+
} catch (error) {
|
|
284
|
+
return toErrorContent(error);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
server2.registerTool(
|
|
289
|
+
"atbash_get_tool_call_count",
|
|
290
|
+
{
|
|
291
|
+
description: "Get the total number of tool calls on-chain.",
|
|
292
|
+
inputSchema: z3.object({})
|
|
293
|
+
},
|
|
294
|
+
async () => {
|
|
295
|
+
try {
|
|
296
|
+
return toJsonContent({ count: await getToolCallCount(opts) });
|
|
297
|
+
} catch (error) {
|
|
298
|
+
return toErrorContent(error);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
);
|
|
302
|
+
server2.registerTool(
|
|
303
|
+
"atbash_get_tier_info",
|
|
304
|
+
{
|
|
305
|
+
description: "Get an organization's tier information.",
|
|
306
|
+
inputSchema: z3.object({
|
|
307
|
+
org_name: z3.string().describe("Organization name")
|
|
308
|
+
})
|
|
309
|
+
},
|
|
310
|
+
async ({ org_name }) => {
|
|
311
|
+
try {
|
|
312
|
+
return toJsonContent(await getOrgTierInfo(org_name, opts));
|
|
313
|
+
} catch (error) {
|
|
314
|
+
return toErrorContent(error);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
server2.registerTool(
|
|
319
|
+
"atbash_get_held_actions",
|
|
320
|
+
{
|
|
321
|
+
description: "List actions currently waiting for operator review.",
|
|
322
|
+
inputSchema: z3.object({
|
|
323
|
+
org_name: z3.string().describe("Organization name"),
|
|
324
|
+
max_count: z3.number().int().positive().optional().describe("Maximum number of records")
|
|
325
|
+
})
|
|
326
|
+
},
|
|
327
|
+
async ({ org_name, max_count }) => {
|
|
328
|
+
try {
|
|
329
|
+
return toJsonContent(
|
|
330
|
+
await getPendingHeldActions(org_name, defaultMaxCount(max_count), opts)
|
|
331
|
+
);
|
|
332
|
+
} catch (error) {
|
|
333
|
+
return toErrorContent(error);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
server2.registerTool(
|
|
338
|
+
"atbash_get_reviews",
|
|
339
|
+
{
|
|
340
|
+
description: "List completed operator reviews for held actions.",
|
|
341
|
+
inputSchema: z3.object({
|
|
342
|
+
org_name: z3.string().describe("Organization name"),
|
|
343
|
+
max_count: z3.number().int().positive().optional().describe("Maximum number of records")
|
|
344
|
+
})
|
|
345
|
+
},
|
|
346
|
+
async ({ org_name, max_count }) => {
|
|
347
|
+
try {
|
|
348
|
+
return toJsonContent(await getHeldActionReviews(org_name, defaultMaxCount(max_count), opts));
|
|
349
|
+
} catch (error) {
|
|
350
|
+
return toErrorContent(error);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
server2.registerTool(
|
|
355
|
+
"atbash_get_safety_stats",
|
|
356
|
+
{
|
|
357
|
+
description: "Get high-level chain-wide safety statistics.",
|
|
358
|
+
inputSchema: z3.object({})
|
|
359
|
+
},
|
|
360
|
+
async () => {
|
|
361
|
+
try {
|
|
362
|
+
return toJsonContent(await getSafetyStats(opts));
|
|
363
|
+
} catch (error) {
|
|
364
|
+
return toErrorContent(error);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/tools/status.ts
|
|
371
|
+
import { z as z4 } from "zod";
|
|
372
|
+
function normalizeVerdict(raw) {
|
|
373
|
+
if (raw == null) return "No verdict";
|
|
374
|
+
const value = String(raw).toUpperCase();
|
|
375
|
+
if (value === "ALLOW" || value === "GREEN") return "ALLOW";
|
|
376
|
+
if (value === "HOLD" || value === "YELLOW") return "HOLD";
|
|
377
|
+
if (value === "BLOCK" || value === "RED") return "BLOCK";
|
|
378
|
+
return value;
|
|
379
|
+
}
|
|
380
|
+
function normalizeStatus(raw) {
|
|
381
|
+
const value = String(raw ?? "").toLowerCase();
|
|
382
|
+
if (value === "pending" || value === "answered" || value === "error") {
|
|
383
|
+
return value;
|
|
384
|
+
}
|
|
385
|
+
return "error";
|
|
386
|
+
}
|
|
387
|
+
function registerStatusTools(server2, defaultPubkey, endpoint2) {
|
|
388
|
+
const baseUrl = (endpoint2 ?? "https://atbash.ai").replace(/\/$/, "");
|
|
389
|
+
server2.registerTool(
|
|
390
|
+
"atbash_judgment_status",
|
|
391
|
+
{
|
|
392
|
+
description: "Poll the status of a previously submitted judgment.",
|
|
393
|
+
inputSchema: z4.object({
|
|
394
|
+
tool_call_id: z4.string().describe("The tool_call_id returned from atbash_judge"),
|
|
395
|
+
agent_pubkey: z4.string().optional().describe("Agent public key, defaults to the server agent")
|
|
396
|
+
})
|
|
397
|
+
},
|
|
398
|
+
async ({ tool_call_id, agent_pubkey }) => {
|
|
399
|
+
try {
|
|
400
|
+
const pubkey = agent_pubkey ?? defaultPubkey;
|
|
401
|
+
const url = new URL(`${baseUrl}/api/v1/judge`);
|
|
402
|
+
url.searchParams.set("tool_call_id", tool_call_id);
|
|
403
|
+
url.searchParams.set("agent_pubkey", pubkey);
|
|
404
|
+
const response = await fetch(url, {
|
|
405
|
+
method: "GET",
|
|
406
|
+
headers: { Accept: "application/json" }
|
|
407
|
+
});
|
|
408
|
+
if (!response.ok) {
|
|
409
|
+
const body = await response.text().catch(() => "");
|
|
410
|
+
throw new Error(`API error ${response.status}: ${body || response.statusText}`);
|
|
411
|
+
}
|
|
412
|
+
const data = await response.json();
|
|
413
|
+
return toJsonContent({
|
|
414
|
+
status: normalizeStatus(data.status),
|
|
415
|
+
verdict: normalizeVerdict(data.verdict),
|
|
416
|
+
reason: String(data.reason ?? ""),
|
|
417
|
+
judgmentId: String(data.judgmentId ?? tool_call_id),
|
|
418
|
+
onChain: Boolean(data.onChain),
|
|
419
|
+
cached: Boolean(data.cached),
|
|
420
|
+
responseTimeMs: Number(data.responseTimeMs ?? 0),
|
|
421
|
+
tool_call_id,
|
|
422
|
+
agent_pubkey: pubkey
|
|
423
|
+
});
|
|
424
|
+
} catch (error) {
|
|
425
|
+
return toErrorContent(error);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/index.ts
|
|
432
|
+
var privkey = process.env.ATBASH_AGENT_PRIVKEY;
|
|
433
|
+
if (!privkey) {
|
|
434
|
+
console.error("ATBASH_AGENT_PRIVKEY env var is required");
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
var agent = loadAgent(privkey);
|
|
438
|
+
var endpoint = process.env.ATBASH_ENDPOINT;
|
|
439
|
+
var server = new McpServer({
|
|
440
|
+
name: "atbash-safety",
|
|
441
|
+
version: "1.0.0"
|
|
442
|
+
});
|
|
443
|
+
registerJudgeTools(server, agent, endpoint);
|
|
444
|
+
registerStatusTools(server, agent.pubkey, endpoint);
|
|
445
|
+
registerQueryTools(server, agent, endpoint);
|
|
446
|
+
registerPolicyResource(server, agent, endpoint);
|
|
447
|
+
registerSafetyPrompt(server);
|
|
448
|
+
var transport = new StdioServerTransport();
|
|
449
|
+
await server.connect(transport);
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atbash/atbash-mcp",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Atbash safety judge exposed as a standalone MCP server",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"atbash-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18.0.0"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"atbash",
|
|
28
|
+
"mcp",
|
|
29
|
+
"ai-safety",
|
|
30
|
+
"agent-safety",
|
|
31
|
+
"model-context-protocol",
|
|
32
|
+
"judge",
|
|
33
|
+
"policy"
|
|
34
|
+
],
|
|
35
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
38
|
+
"example": "npm run build && npm install --prefix examples/mcp-runtime-agent && node examples/mcp-runtime-agent/client.mjs",
|
|
39
|
+
"prepublishOnly": "npm run build",
|
|
40
|
+
"publish:prod": "npm publish --tag latest --access public",
|
|
41
|
+
"publish:dev": "node scripts/publish-dev.mjs"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@atbash/sdk": "^0.3.20",
|
|
45
|
+
"@modelcontextprotocol/sdk": "^1.12.3",
|
|
46
|
+
"zod": "^3.25.76"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^25.7.0",
|
|
50
|
+
"tsup": "^8.0.0",
|
|
51
|
+
"typescript": "^5.0.0"
|
|
52
|
+
}
|
|
53
|
+
}
|