@toknbase/mcp-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +84 -0
  2. package/index.js +283 -0
  3. package/package.json +24 -0
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # @toknbase/mcp-server
2
+
3
+ Zero-knowledge secrets management for AI agents via the [Model Context Protocol](https://modelcontextprotocol.io).
4
+
5
+ Toknbase is the only secrets manager where the host never sees your plaintext secrets. This MCP server connects your AI coding assistant to your Toknbase vault -- read, create, update, and delete secrets directly from Cursor, Windsurf, Claude Code, VS Code, Zed, and Cline.
6
+
7
+ ## Quick Start
8
+
9
+ ### 1. Create an Agent Token
10
+
11
+ Log in to your Toknbase dashboard and navigate to **API Tokens → Agent Tokens**. Create a new token with the scope you need:
12
+
13
+ - `read_only` -- list and reveal secrets
14
+ - `read_write` -- list, reveal, create, and update secrets
15
+ - `full_access` -- all of the above plus delete and folder management
16
+
17
+ Copy the `agt_` token value -- it is shown only once.
18
+
19
+ ### 2. Add to Your Editor
20
+
21
+ Replace `agt_your_token_here` with your token and `YOUR_CANISTER_ID` with your Toknbase canister ID (visible in the dashboard).
22
+
23
+ **Cursor** (`~/.cursor/mcp.json`)
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "toknbase": {
28
+ "command": "npx",
29
+ "args": ["-y", "@toknbase/mcp-server"],
30
+ "env": {
31
+ "TOKNBASE_AGENT_TOKEN": "agt_your_token_here",
32
+ "TOKNBASE_CANISTER_ID": "YOUR_CANISTER_ID"
33
+ }
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ **Claude Code** (`~/.claude.json`, mcpServers section)
40
+ ```json
41
+ {
42
+ "mcpServers": {
43
+ "toknbase": {
44
+ "command": "npx",
45
+ "args": ["-y", "@toknbase/mcp-server"],
46
+ "env": {
47
+ "TOKNBASE_AGENT_TOKEN": "agt_your_token_here",
48
+ "TOKNBASE_CANISTER_ID": "YOUR_CANISTER_ID"
49
+ }
50
+ }
51
+ }
52
+ }
53
+ ```
54
+
55
+ ## Available Tools
56
+
57
+ | Tool | Description | Required Scope |
58
+ |---|---|---|
59
+ | `toknbase_list_secrets` | List all secrets (names, descriptions, environments -- no values) | read_only |
60
+ | `toknbase_reveal_secret` | Retrieve the value of a specific secret by name | read_only |
61
+ | `toknbase_create_secret` | Add a new secret to the vault | read_write |
62
+ | `toknbase_update_secret` | Update a secret's value | read_write |
63
+ | `toknbase_delete_secret` | Permanently delete a secret | full_access |
64
+ | `toknbase_list_folders` | List all folders | read_only |
65
+ | `toknbase_assign_folder` | Assign a secret to a folder | full_access |
66
+
67
+ ## Environment Variables
68
+
69
+ | Variable | Required | Description |
70
+ |---|---|---|
71
+ | `TOKNBASE_AGENT_TOKEN` | Yes | Your `agt_` agent token from the dashboard |
72
+ | `TOKNBASE_CANISTER_ID` | Yes | Your Toknbase canister ID |
73
+ | `TOKNBASE_IC_HOST` | No | IC host URL (default: `https://ic0.app`) |
74
+
75
+ ## Security
76
+
77
+ Toknbase is zero-knowledge -- your plaintext secrets never leave your device. The MCP server calls the Toknbase canister which returns encrypted values. For `reveal_secret`, the value returned is the stored encrypted blob; decryption happens in your local environment using your client-side key.
78
+
79
+ Every action taken by an agent token is recorded in your Toknbase cryptographic audit log under the token's name.
80
+
81
+ ## Requirements
82
+
83
+ - Node.js 18+
84
+ - A Toknbase account with an active agent token
package/index.js ADDED
@@ -0,0 +1,283 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @toknbase/mcp-server
4
+ * Zero-knowledge secrets management for AI agents via Model Context Protocol.
5
+ *
6
+ * Required env vars:
7
+ * TOKNBASE_AGENT_TOKEN -- your agt_ scoped agent token from the Toknbase dashboard
8
+ * TOKNBASE_CANISTER_ID -- your Toknbase canister ID (e.g. xxxxx-xxxxx-cai)
9
+ *
10
+ * Optional:
11
+ * TOKNBASE_IC_HOST -- IC host (default: https://ic0.app)
12
+ */
13
+
14
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
15
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
16
+ import {
17
+ CallToolRequestSchema,
18
+ ListToolsRequestSchema,
19
+ } from "@modelcontextprotocol/sdk/types.js";
20
+ import { Actor, HttpAgent } from "@dfinity/agent";
21
+ import { IDL } from "@dfinity/candid";
22
+
23
+ // ── Config ──────────────────────────────────────────────────────────────────
24
+ const TOKEN = process.env.TOKNBASE_AGENT_TOKEN;
25
+ const CANISTER_ID = process.env.TOKNBASE_CANISTER_ID;
26
+ const IC_HOST = process.env.TOKNBASE_IC_HOST ?? "https://ic0.app";
27
+
28
+ if (!TOKEN) {
29
+ console.error("[toknbase-mcp] Missing TOKNBASE_AGENT_TOKEN environment variable.");
30
+ process.exit(1);
31
+ }
32
+ if (!CANISTER_ID) {
33
+ console.error("[toknbase-mcp] Missing TOKNBASE_CANISTER_ID environment variable.");
34
+ process.exit(1);
35
+ }
36
+
37
+ // ── Candid IDL (agent-token surface only) ───────────────────────────────────
38
+ const idlFactory = ({ IDL: I }) => {
39
+ const SecretMeta = I.Record({
40
+ name: I.Text,
41
+ description: I.Text,
42
+ environment: I.Text,
43
+ });
44
+ const FolderMeta = I.Record({
45
+ id: I.Nat,
46
+ name: I.Text,
47
+ });
48
+ return I.Service({
49
+ listSecretsByAgentToken: I.Func([I.Text], [I.Vec(SecretMeta)], ["query"]),
50
+ createSecretByAgentToken: I.Func(
51
+ [I.Text, I.Text, I.Text, I.Text, I.Text],
52
+ [I.Bool],
53
+ []
54
+ ),
55
+ updateSecretByAgentToken: I.Func([I.Text, I.Text, I.Text], [I.Bool], []),
56
+ deleteSecretByAgentToken: I.Func([I.Text, I.Text], [I.Bool], []),
57
+ listFoldersByAgentToken: I.Func([I.Text], [I.Vec(FolderMeta)], ["query"]),
58
+ assignFolderByAgentToken: I.Func([I.Text, I.Nat, I.Text], [I.Bool], []),
59
+ getSecretByAgentToken: I.Func([I.Text, I.Text], [I.Opt(I.Text)], ["query"]),
60
+ });
61
+ };
62
+
63
+ // ── IC Actor ─────────────────────────────────────────────────────────────────
64
+ const agent = new HttpAgent({ host: IC_HOST });
65
+ // Fetch root key only on local replica
66
+ if (IC_HOST !== "https://ic0.app") {
67
+ await agent.fetchRootKey();
68
+ }
69
+
70
+ const backend = Actor.createActor(idlFactory, { agent, canisterId: CANISTER_ID });
71
+
72
+ // ── MCP Server ───────────────────────────────────────────────────────────────
73
+ const server = new Server(
74
+ { name: "toknbase", version: "1.0.0" },
75
+ { capabilities: { tools: {} } }
76
+ );
77
+
78
+ // Tool definitions
79
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
80
+ tools: [
81
+ {
82
+ name: "toknbase_list_secrets",
83
+ description: "List all secrets you have access to. Returns names, descriptions, and environments -- never plaintext values.",
84
+ inputSchema: { type: "object", properties: {}, required: [] },
85
+ },
86
+ {
87
+ name: "toknbase_reveal_secret",
88
+ description: "Reveal the encrypted value of a specific secret by name. Use this only when you need the actual value.",
89
+ inputSchema: {
90
+ type: "object",
91
+ properties: {
92
+ name: { type: "string", description: "The exact secret name" },
93
+ },
94
+ required: ["name"],
95
+ },
96
+ },
97
+ {
98
+ name: "toknbase_create_secret",
99
+ description: "Add a new secret to the vault. Requires read_write or full_access scope.",
100
+ inputSchema: {
101
+ type: "object",
102
+ properties: {
103
+ name: { type: "string", description: "Secret name (e.g. STRIPE_SECRET_KEY)" },
104
+ value: { type: "string", description: "The secret value to store" },
105
+ description: { type: "string", description: "Optional description" },
106
+ environment: {
107
+ type: "string",
108
+ enum: ["production", "staging", "development"],
109
+ description: "Environment tag",
110
+ },
111
+ },
112
+ required: ["name", "value"],
113
+ },
114
+ },
115
+ {
116
+ name: "toknbase_update_secret",
117
+ description: "Update the value of an existing secret. Requires read_write or full_access scope.",
118
+ inputSchema: {
119
+ type: "object",
120
+ properties: {
121
+ name: { type: "string", description: "The exact secret name to update" },
122
+ value: { type: "string", description: "The new secret value" },
123
+ },
124
+ required: ["name", "value"],
125
+ },
126
+ },
127
+ {
128
+ name: "toknbase_delete_secret",
129
+ description: "Permanently delete a secret from the vault. Requires full_access scope. This cannot be undone.",
130
+ inputSchema: {
131
+ type: "object",
132
+ properties: {
133
+ name: { type: "string", description: "The exact secret name to delete" },
134
+ },
135
+ required: ["name"],
136
+ },
137
+ },
138
+ {
139
+ name: "toknbase_list_folders",
140
+ description: "List all folders in the vault.",
141
+ inputSchema: { type: "object", properties: {}, required: [] },
142
+ },
143
+ {
144
+ name: "toknbase_assign_folder",
145
+ description: "Assign a secret to a folder. Requires full_access scope.",
146
+ inputSchema: {
147
+ type: "object",
148
+ properties: {
149
+ secret_name: { type: "string", description: "The secret name to assign" },
150
+ folder_id: { type: "number", description: "The numeric folder ID" },
151
+ },
152
+ required: ["secret_name", "folder_id"],
153
+ },
154
+ },
155
+ ],
156
+ }));
157
+
158
+ // Tool handlers
159
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
160
+ const { name, arguments: args } = request.params;
161
+
162
+ try {
163
+ switch (name) {
164
+ case "toknbase_list_secrets": {
165
+ const secrets = await backend.listSecretsByAgentToken(TOKEN);
166
+ return {
167
+ content: [
168
+ {
169
+ type: "text",
170
+ text: JSON.stringify(secrets, null, 2),
171
+ },
172
+ ],
173
+ };
174
+ }
175
+
176
+ case "toknbase_reveal_secret": {
177
+ const result = await backend.getSecretByAgentToken(args.name, TOKEN);
178
+ if (result.length === 0 || result[0] === undefined) {
179
+ return {
180
+ content: [{ type: "text", text: `Secret '${args.name}' not found or access denied.` }],
181
+ isError: true,
182
+ };
183
+ }
184
+ return {
185
+ content: [{ type: "text", text: result[0] }],
186
+ };
187
+ }
188
+
189
+ case "toknbase_create_secret": {
190
+ const ok = await backend.createSecretByAgentToken(
191
+ args.name,
192
+ args.value,
193
+ args.description ?? "",
194
+ args.environment ?? "development",
195
+ TOKEN
196
+ );
197
+ return {
198
+ content: [
199
+ {
200
+ type: "text",
201
+ text: ok
202
+ ? `Secret '${args.name}' created successfully.`
203
+ : `Failed to create secret '${args.name}'. Check your token scope (requires read_write or full_access).`,
204
+ },
205
+ ],
206
+ isError: !ok,
207
+ };
208
+ }
209
+
210
+ case "toknbase_update_secret": {
211
+ const ok = await backend.updateSecretByAgentToken(args.name, args.value, TOKEN);
212
+ return {
213
+ content: [
214
+ {
215
+ type: "text",
216
+ text: ok
217
+ ? `Secret '${args.name}' updated successfully.`
218
+ : `Failed to update secret '${args.name}'. Check that it exists and your token scope (requires read_write or full_access).`,
219
+ },
220
+ ],
221
+ isError: !ok,
222
+ };
223
+ }
224
+
225
+ case "toknbase_delete_secret": {
226
+ const ok = await backend.deleteSecretByAgentToken(args.name, TOKEN);
227
+ return {
228
+ content: [
229
+ {
230
+ type: "text",
231
+ text: ok
232
+ ? `Secret '${args.name}' deleted.`
233
+ : `Failed to delete secret '${args.name}'. Check that it exists and your token scope (requires full_access).`,
234
+ },
235
+ ],
236
+ isError: !ok,
237
+ };
238
+ }
239
+
240
+ case "toknbase_list_folders": {
241
+ const folders = await backend.listFoldersByAgentToken(TOKEN);
242
+ return {
243
+ content: [{ type: "text", text: JSON.stringify(folders, null, 2) }],
244
+ };
245
+ }
246
+
247
+ case "toknbase_assign_folder": {
248
+ const ok = await backend.assignFolderByAgentToken(
249
+ args.secret_name,
250
+ BigInt(args.folder_id),
251
+ TOKEN
252
+ );
253
+ return {
254
+ content: [
255
+ {
256
+ type: "text",
257
+ text: ok
258
+ ? `Secret '${args.secret_name}' assigned to folder ${args.folder_id}.`
259
+ : `Failed to assign folder. Check token scope (requires full_access).`,
260
+ },
261
+ ],
262
+ isError: !ok,
263
+ };
264
+ }
265
+
266
+ default:
267
+ return {
268
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
269
+ isError: true,
270
+ };
271
+ }
272
+ } catch (err) {
273
+ return {
274
+ content: [{ type: "text", text: `Error: ${err.message}` }],
275
+ isError: true,
276
+ };
277
+ }
278
+ });
279
+
280
+ // ── Start ────────────────────────────────────────────────────────────────────
281
+ const transport = new StdioServerTransport();
282
+ await server.connect(transport);
283
+ console.error("[toknbase-mcp] Server running. Canister:", CANISTER_ID);
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@toknbase/mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Toknbase -- zero-knowledge secrets management for AI agents",
5
+ "type": "module",
6
+ "bin": {
7
+ "toknbase-mcp-server": "./index.js"
8
+ },
9
+ "main": "./index.js",
10
+ "scripts": {
11
+ "start": "node index.js"
12
+ },
13
+ "dependencies": {
14
+ "@modelcontextprotocol/sdk": "^1.0.0",
15
+ "@dfinity/agent": "^2.1.3",
16
+ "@dfinity/candid": "^2.1.3",
17
+ "@dfinity/principal": "^2.1.3"
18
+ },
19
+ "keywords": ["mcp", "secrets", "toknbase", "icp", "zero-knowledge", "ai-agent"],
20
+ "license": "MIT",
21
+ "engines": {
22
+ "node": ">=18.0.0"
23
+ }
24
+ }