@nodatachat/mcp 0.4.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.
package/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # @nodatachat/mcp
2
+
3
+ MCP (Model Context Protocol) server for **governing data given to AI agents** — plus NoData's Blind Relay (encrypt / decrypt / deliver / evidence).
4
+
5
+ ## Two modes — the credential decides what the server can do
6
+
7
+ **Agent mode (`--grant-token ndca-...`)** — *permission management for language models.*
8
+ You hand an AI agent a grant token; this server exposes tools hard-scoped to that grant: `nodata_decide` (may I reach this? — decision + proof, no data), `nodata_retrieve` (authorized context, filtered to what you may see), `nodata_read` (read governed rows), and `nodata_use` (secret-blind invoke). The agent may only request columns its grant allows — **denied columns never decrypt server-side**, and every action emits a cryptographic receipt. Point any MCP client (Claude, an agent runtime) at it and it physically cannot exceed its claims.
9
+
10
+ The plan-then-act loop: **`nodata_decide`** to check reachability (allow / degrade / deny + access-distance cost), then **`nodata_retrieve`** to pull only the authorized context into your prompt.
11
+
12
+ ```bash
13
+ claude mcp add nodata -- npx @nodatachat/mcp --grant-token ndca-YOUR_GRANT
14
+ ```
15
+
16
+ **Admin mode (`--api-key sk_live_...`)** — manage governance from inside any MCP client: register your tables, issue grants, revoke them, and use blind relay.
17
+
18
+ ```bash
19
+ claude mcp add nodata -- npx @nodatachat/mcp --api-key YOUR_API_KEY
20
+ ```
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install @nodatachat/mcp
26
+ ```
27
+
28
+ Or run directly with npx:
29
+
30
+ ```bash
31
+ npx @nodatachat/mcp --api-key sk_live_your_key_here
32
+ ```
33
+
34
+ ## Claude Code Setup
35
+
36
+ Add the server to Claude Code in one command:
37
+
38
+ ```bash
39
+ claude mcp add nodata -- npx @nodatachat/mcp --api-key YOUR_API_KEY
40
+ ```
41
+
42
+ Or with a custom base URL (for self-hosted or staging):
43
+
44
+ ```bash
45
+ claude mcp add nodata -- npx @nodatachat/mcp --api-key YOUR_API_KEY --base-url https://staging.nodatacapsule.com
46
+ ```
47
+
48
+ ## Configuration
49
+
50
+ | Option | Flag | Env Var | Default |
51
+ |--------|------|---------|---------|
52
+ | API Key (admin) | `--api-key` | `NODATA_API_KEY` | — |
53
+ | Grant token (agent) | `--grant-token` | `NODATA_GRANT_TOKEN` | — |
54
+ | Base URL | `--base-url` | `NODATA_BASE_URL` | `https://www.nodatacapsule.com` |
55
+
56
+ At least one of `--api-key` or `--grant-token` is required. API keys use the format `sk_live_…`; grant tokens are `ndca-…` (mint via `POST /api/v1/governance/grant`).
57
+
58
+ ## Available Tools
59
+
60
+ **Agent mode** (`--grant-token`):
61
+
62
+ | Tool | Description |
63
+ |------|-------------|
64
+ | `nodata_decide` | Ask whether the agent MAY reach a resource, without reading it. Returns allow / degrade (reachable subset) / deny + an access-distance cost + a signed proof. No data. Plan before you read. |
65
+ | `nodata_retrieve` | Prompt-ready authorized context, filtered to exactly the allowed columns (denied columns never decrypt), with an accounting of what was withheld + a signed proof. |
66
+ | `nodata_read` | Read governed data within this agent's grant. Denied columns never decrypt; every read emits a receipt. Hard-scoped to the grant token. |
67
+ | `nodata_use` | Invoke a pre-registered secret-blind capability; the Capsule injects the sealed credential server-side and returns only the result. The secret never reaches the agent. |
68
+
69
+ **Admin mode** (`--api-key`):
70
+
71
+ | Tool | Description |
72
+ |------|-------------|
73
+ | `nodata_register_table` | Register one of your tables as AI-agent-readable ("connect your data"). |
74
+ | `nodata_grant` | Issue an agent a claim-scoped grant; returns a grant_token (shown once). |
75
+ | `nodata_revoke` | Revoke a grant by handle or jti. |
76
+ | `nodata_encrypt` | Encrypt a field (AES-256-GCM). Returns ciphertext for you to store. NoData stores nothing. |
77
+ | `nodata_decrypt` | Decrypt ciphertext back to plaintext. |
78
+ | `nodata_deliver` | Create a burn-after-read secure link with OTP. |
79
+ | `nodata_evidence` | Retrieve audit trail (metadata only). |
80
+
81
+ ## Example Usage in Claude Code
82
+
83
+ Once the MCP server is added, you can use natural language:
84
+
85
+ - "Encrypt my database password using NoData"
86
+ - "Create a secure link to send this API key to the DevOps team"
87
+ - "Show me the audit trail for the last 7 days"
88
+ - "Decrypt this ciphertext: aes256gcm:v1:..."
89
+
90
+ ## Development
91
+
92
+ ```bash
93
+ cd packages/mcp-server
94
+ npm install
95
+ npm run dev -- --api-key sk_live_test_key
96
+ ```
97
+
98
+ Build for production:
99
+
100
+ ```bash
101
+ npm run build
102
+ ```
103
+
104
+ ## License
105
+
106
+ MIT
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * NoData MCP Server
4
+ *
5
+ * Two modes — the credential you give it decides what it can do:
6
+ *
7
+ * ADMIN MODE (--api-key sk_live_...)
8
+ * Manage governance + blind-relay from inside any MCP client:
9
+ * nodata_register_table · nodata_grant · nodata_revoke
10
+ * nodata_encrypt · nodata_decrypt · nodata_deliver · nodata_evidence
11
+ *
12
+ * AGENT MODE (--grant-token ndca-...)
13
+ * This is the "permission management for language models" artifact.
14
+ * You hand an AI agent a grant token; this server then exposes tools
15
+ * scoped to exactly that grant:
16
+ * nodata_decide — ask "may I reach this?" and get a decision + proof,
17
+ * never any data (plan before you read).
18
+ * nodata_retrieve — get prompt-ready context filtered to what you may
19
+ * see, with an accounting of what was withheld.
20
+ * nodata_read — read governed data (denied columns never decrypt).
21
+ * nodata_use — invoke a secret-blind capability: the Capsule injects
22
+ * the sealed credential server-side and returns only the
23
+ * result; the agent never receives the secret.
24
+ * Every decide/retrieve/read/invoke emits a receipt. Point any MCP client
25
+ * (Claude, an agent runtime) at it: it physically cannot exceed its grant.
26
+ *
27
+ * Usage:
28
+ * nodata-mcp --api-key sk_live_xxx
29
+ * nodata-mcp --grant-token ndca-xxx [--base-url https://www.nodatacapsule.com]
30
+ */
31
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,363 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * NoData MCP Server
4
+ *
5
+ * Two modes — the credential you give it decides what it can do:
6
+ *
7
+ * ADMIN MODE (--api-key sk_live_...)
8
+ * Manage governance + blind-relay from inside any MCP client:
9
+ * nodata_register_table · nodata_grant · nodata_revoke
10
+ * nodata_encrypt · nodata_decrypt · nodata_deliver · nodata_evidence
11
+ *
12
+ * AGENT MODE (--grant-token ndca-...)
13
+ * This is the "permission management for language models" artifact.
14
+ * You hand an AI agent a grant token; this server then exposes tools
15
+ * scoped to exactly that grant:
16
+ * nodata_decide — ask "may I reach this?" and get a decision + proof,
17
+ * never any data (plan before you read).
18
+ * nodata_retrieve — get prompt-ready context filtered to what you may
19
+ * see, with an accounting of what was withheld.
20
+ * nodata_read — read governed data (denied columns never decrypt).
21
+ * nodata_use — invoke a secret-blind capability: the Capsule injects
22
+ * the sealed credential server-side and returns only the
23
+ * result; the agent never receives the secret.
24
+ * Every decide/retrieve/read/invoke emits a receipt. Point any MCP client
25
+ * (Claude, an agent runtime) at it: it physically cannot exceed its grant.
26
+ *
27
+ * Usage:
28
+ * nodata-mcp --api-key sk_live_xxx
29
+ * nodata-mcp --grant-token ndca-xxx [--base-url https://www.nodatacapsule.com]
30
+ */
31
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
32
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
33
+ import { z } from "zod";
34
+ function parseArgs(argv) {
35
+ let apiKey = "";
36
+ let grantToken = "";
37
+ let baseUrl = "https://www.nodatacapsule.com";
38
+ for (let i = 0; i < argv.length; i++) {
39
+ if (argv[i] === "--api-key" && argv[i + 1]) {
40
+ apiKey = argv[++i];
41
+ }
42
+ else if (argv[i] === "--grant-token" && argv[i + 1]) {
43
+ grantToken = argv[++i];
44
+ }
45
+ else if (argv[i] === "--base-url" && argv[i + 1]) {
46
+ baseUrl = argv[++i];
47
+ }
48
+ }
49
+ if (!apiKey)
50
+ apiKey = process.env.NODATA_API_KEY ?? "";
51
+ if (!grantToken)
52
+ grantToken = process.env.NODATA_GRANT_TOKEN ?? "";
53
+ if (process.env.NODATA_BASE_URL)
54
+ baseUrl = process.env.NODATA_BASE_URL;
55
+ if (!apiKey && !grantToken) {
56
+ console.error("Error: a credential is required.\n\n" +
57
+ " ADMIN mode: nodata-mcp --api-key sk_live_...\n" +
58
+ " AGENT mode: nodata-mcp --grant-token ndca-...\n\n" +
59
+ "Get an API key at https://www.nodatacapsule.com/developers/get-started\n" +
60
+ "Mint a grant token via POST /api/v1/governance/grant.");
61
+ process.exit(1);
62
+ }
63
+ baseUrl = baseUrl.replace(/\/+$/, "");
64
+ return { apiKey, grantToken, baseUrl };
65
+ }
66
+ async function apiPost(baseUrl, bearer, path, body) {
67
+ const res = await fetch(`${baseUrl}${path}`, {
68
+ method: "POST",
69
+ headers: {
70
+ "Content-Type": "application/json",
71
+ Authorization: `Bearer ${bearer}`,
72
+ },
73
+ body: JSON.stringify(body),
74
+ });
75
+ let parsed;
76
+ try {
77
+ parsed = await res.json();
78
+ }
79
+ catch {
80
+ parsed = { error: `Non-JSON response (HTTP ${res.status})` };
81
+ }
82
+ return { ok: res.ok, status: res.status, body: parsed };
83
+ }
84
+ async function apiGet(baseUrl, bearer, path, params) {
85
+ const url = new URL(`${baseUrl}${path}`);
86
+ for (const [key, val] of Object.entries(params)) {
87
+ if (val !== undefined && val !== "")
88
+ url.searchParams.set(key, val);
89
+ }
90
+ const res = await fetch(url.toString(), {
91
+ method: "GET",
92
+ headers: { Authorization: `Bearer ${bearer}` },
93
+ });
94
+ let parsed;
95
+ try {
96
+ parsed = await res.json();
97
+ }
98
+ catch {
99
+ parsed = { error: `Non-JSON response (HTTP ${res.status})` };
100
+ }
101
+ return { ok: res.ok, status: res.status, body: parsed };
102
+ }
103
+ function formatResult(resp) {
104
+ const text = JSON.stringify(resp.body, null, 2);
105
+ if (!resp.ok) {
106
+ return {
107
+ content: [{ type: "text", text: `Error (HTTP ${resp.status}):\n${text}` }],
108
+ isError: true,
109
+ };
110
+ }
111
+ return { content: [{ type: "text", text }] };
112
+ }
113
+ /** Decode the handle out of an agent grant token (ndca-<jwt>). */
114
+ function handleFromGrant(token) {
115
+ try {
116
+ const jwt = token.startsWith("ndca-") ? token.slice(5) : token;
117
+ const payload = jwt.split(".")[1];
118
+ if (!payload)
119
+ return null;
120
+ const json = JSON.parse(Buffer.from(payload.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf8"));
121
+ return json.handle ?? null;
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
127
+ // ---------------------------------------------------------------------------
128
+ // Tool registration
129
+ // ---------------------------------------------------------------------------
130
+ function registerAdminTools(server, baseUrl, apiKey) {
131
+ // ── Governance ──────────────────────────────────────────────────────
132
+ server.registerTool("nodata_register_table", {
133
+ description: "Register one of your own tables as AI-agent-readable ('connect your data'). Declares which columns are encrypted (per-column key release) and which are plaintext metadata. After this, grants can target the table.",
134
+ inputSchema: {
135
+ table_name: z.string().describe("Your SQL table name"),
136
+ encrypted_columns: z.array(z.string()).describe("Columns stored as <col>_ct/_iv/_tag triplets"),
137
+ plaintext_columns: z.array(z.string()).optional().describe("Plaintext metadata/filter columns"),
138
+ classification_column: z.string().optional().describe("Plaintext column carrying the classification value"),
139
+ },
140
+ }, async ({ table_name, encrypted_columns, plaintext_columns, classification_column }) => {
141
+ const resp = await apiPost(baseUrl, apiKey, "/api/v1/governance/tables", {
142
+ table_name,
143
+ encrypted_columns,
144
+ plaintext_columns: plaintext_columns ?? [],
145
+ classification_column,
146
+ });
147
+ return formatResult(resp);
148
+ });
149
+ server.registerTool("nodata_grant", {
150
+ description: "Issue an AI agent a claim-scoped grant over your data — claims, not blanket access. Returns a grant_token (shown once) the agent presents to read. Denied columns never decrypt.",
151
+ inputSchema: {
152
+ agent: z.string().describe("Agent label (becomes its handle)"),
153
+ table: z.string().optional().describe("Target table (default agent_demo_records)"),
154
+ columns: z.array(z.string()).describe("Columns the agent may read"),
155
+ where: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional().describe("Equality filter (plaintext cols only)"),
156
+ classifications: z.array(z.string()).optional().describe("Allowed classification values; empty = no gating"),
157
+ ttl_seconds: z.number().optional().describe("Token lifetime (default 30d, max 1y)"),
158
+ },
159
+ }, async ({ agent, table, columns, where, classifications, ttl_seconds }) => {
160
+ const resp = await apiPost(baseUrl, apiKey, "/api/v1/governance/grant", {
161
+ agent,
162
+ table,
163
+ columns,
164
+ where,
165
+ classifications,
166
+ ttl_seconds,
167
+ });
168
+ return formatResult(resp);
169
+ });
170
+ server.registerTool("nodata_revoke", {
171
+ description: "Revoke an agent's grant by handle (kills all its active credentials) or by a specific jti. Revocation is mathematics — the next read 401s and any cached data is governance-dead.",
172
+ inputSchema: {
173
+ handle: z.string().optional().describe("Agent handle to revoke"),
174
+ jti: z.string().optional().describe("Specific credential jti to revoke"),
175
+ reason: z.string().optional(),
176
+ },
177
+ }, async ({ handle, jti, reason }) => {
178
+ const resp = await apiPost(baseUrl, apiKey, "/api/v1/governance/revoke", { handle, jti, reason });
179
+ return formatResult(resp);
180
+ });
181
+ // ── Blind relay (kept from v0.1) ────────────────────────────────────
182
+ server.registerTool("nodata_encrypt", {
183
+ description: "Encrypt a sensitive field value using Capsule Blind Relay. Returns ciphertext. NoData stores nothing — the key is embedded in the ciphertext for YOU to store.",
184
+ inputSchema: {
185
+ field: z.string(),
186
+ value: z.string(),
187
+ context: z.string().optional(),
188
+ },
189
+ }, async ({ field, value, context }) => {
190
+ const body = { field, value };
191
+ if (context)
192
+ body.context = context;
193
+ return formatResult(await apiPost(baseUrl, apiKey, "/api/v1/encrypt", body));
194
+ });
195
+ server.registerTool("nodata_decrypt", {
196
+ description: "Decrypt a Capsule ciphertext back to plaintext (aes256gcm:v1:iv:cipher:key format).",
197
+ inputSchema: {
198
+ ciphertext: z.string(),
199
+ field: z.string().optional(),
200
+ requester: z.string().optional(),
201
+ },
202
+ }, async ({ ciphertext, field, requester }) => {
203
+ const body = { ciphertext };
204
+ if (field)
205
+ body.field = field;
206
+ if (requester)
207
+ body.requester = requester;
208
+ return formatResult(await apiPost(baseUrl, apiKey, "/api/v1/decrypt", body));
209
+ });
210
+ server.registerTool("nodata_deliver", {
211
+ description: "Create a burn-after-read secure link for delivering sensitive content. Returns a view URL and OTP.",
212
+ inputSchema: {
213
+ content: z.string(),
214
+ to: z.string().optional(),
215
+ maxViews: z.number().optional(),
216
+ ttl: z.string().optional(),
217
+ burn: z.boolean().optional(),
218
+ },
219
+ }, async ({ content, to, maxViews, ttl, burn }) => {
220
+ const body = { content };
221
+ if (to)
222
+ body.to = to;
223
+ if (maxViews !== undefined)
224
+ body.maxViews = maxViews;
225
+ if (ttl)
226
+ body.ttl = ttl;
227
+ if (burn !== undefined)
228
+ body.burn = burn;
229
+ return formatResult(await apiPost(baseUrl, apiKey, "/api/v1/deliver", body));
230
+ });
231
+ server.registerTool("nodata_evidence", {
232
+ description: "Retrieve the audit trail of governance/encryption events. Metadata only — never content, keys, or plaintext.",
233
+ inputSchema: {
234
+ from: z.string().optional(),
235
+ to: z.string().optional(),
236
+ action: z.string().optional(),
237
+ limit: z.number().optional(),
238
+ },
239
+ }, async ({ from, to, action, limit }) => {
240
+ const params = {
241
+ from,
242
+ to,
243
+ action,
244
+ limit: limit !== undefined ? String(limit) : undefined,
245
+ };
246
+ return formatResult(await apiGet(baseUrl, apiKey, "/api/v1/evidence", params));
247
+ });
248
+ }
249
+ function registerAgentTool(server, baseUrl, grantToken) {
250
+ const handle = handleFromGrant(grantToken);
251
+ // ── Access Compute · the "may I?" oracle (P0) ───────────────────────
252
+ // Returns ONLY a decision + a signed proof, never any content. A planner
253
+ // calls this to learn what it can reach BEFORE moving data: allow, degrade
254
+ // (the reachable subset), or deny — with an access-distance cost. Bearer-only
255
+ // endpoint (no handle in the path).
256
+ server.registerTool("nodata_decide", {
257
+ description: "Ask whether this agent MAY reach a resource, WITHOUT reading it. Returns a decision (allow / degrade / deny), the reachable column subset, an access-distance cost, and a signed proof — never any data. Use it to plan: check reachability before you retrieve. Hard-scoped to this server's grant token.",
258
+ inputSchema: {
259
+ table: z.string().describe("Table to check (must be in your grant)"),
260
+ columns: z.array(z.string()).describe("Columns you want to reach"),
261
+ where: z
262
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
263
+ .optional()
264
+ .describe("Equality filter (readable columns only)"),
265
+ },
266
+ }, async ({ table, columns, where }) => {
267
+ const resp = await apiPost(baseUrl, grantToken, "/api/access/decide", {
268
+ resource: { table, columns, where },
269
+ intent: "read",
270
+ });
271
+ return formatResult(resp);
272
+ });
273
+ // ── Authorized Context · access-aware RAG (P1) ──────────────────────
274
+ // Runs the same decision, then returns prompt-ready context filtered to
275
+ // exactly the allowed columns (denied columns are never decrypted), plus an
276
+ // accounting of what was withheld and a signed proof.
277
+ server.registerTool("nodata_retrieve", {
278
+ description: "Retrieve prompt-ready context for a resource, filtered to exactly what this agent may see. Denied columns are never decrypted; the response includes an accounting of what was withheld (columns + estimated tokens saved) and a signed proof. Prefer this over a raw read when assembling an LLM prompt. Hard-scoped to this server's grant token.",
279
+ inputSchema: {
280
+ table: z.string().describe("Table to retrieve from (must be in your grant)"),
281
+ columns: z.array(z.string()).describe("Columns to include in context"),
282
+ where: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
283
+ top_k: z.number().optional().describe("Max rows (default 50, max 200)"),
284
+ },
285
+ }, async ({ table, columns, where, top_k }) => {
286
+ const resp = await apiPost(baseUrl, grantToken, "/api/access/retrieve", {
287
+ resource: { table, columns, where },
288
+ top_k,
289
+ });
290
+ return formatResult(resp);
291
+ });
292
+ server.registerTool("nodata_read", {
293
+ description: "Read governed data within this agent's grant. You may only request columns your grant allows — denied columns never decrypt server-side, and every read emits a cryptographic receipt. This tool is hard-scoped to the grant token this server was started with.",
294
+ inputSchema: {
295
+ table: z.string().describe("Table to read (must be in your grant)"),
296
+ columns: z.array(z.string()).describe("Columns to read (must be in your grant)"),
297
+ where: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
298
+ limit: z.number().optional().describe("Max rows (default 50, max 200)"),
299
+ },
300
+ }, async ({ table, columns, where, limit }) => {
301
+ if (!handle) {
302
+ return {
303
+ content: [{ type: "text", text: "Error: could not derive agent handle from grant token." }],
304
+ isError: true,
305
+ };
306
+ }
307
+ const resp = await apiPost(baseUrl, grantToken, `/api/agents/${encodeURIComponent(handle)}/read`, {
308
+ table,
309
+ columns,
310
+ where,
311
+ limit,
312
+ });
313
+ return formatResult(resp);
314
+ });
315
+ // ── Secret-blind action (AI Access Plane) ───────────────────────────
316
+ // The headline of the plane: INVOKE a pre-registered capability. The
317
+ // Capsule injects the sealed secret server-side and returns ONLY the
318
+ // result — you (the agent / model) never receive the key. Deny-by-default:
319
+ // you can invoke only capabilities your grant lists. Every invoke emits a
320
+ // signed receipt proving you acted and never held the secret.
321
+ server.registerTool("nodata_use", {
322
+ description: "Invoke a pre-registered capability (deploy, send, charge, query, …) WITHOUT receiving the secret. The Capsule injects the sealed credential server-side and returns only the result. You may invoke only capabilities your grant allows; the secret never reaches you. Hard-scoped to the grant token this server was started with.",
323
+ inputSchema: {
324
+ capability: z.string().describe("Capability name to invoke (must be in your grant, e.g. 'deploy-prod')"),
325
+ params: z.record(z.string(), z.unknown()).optional().describe("Allowlisted params; unknown keys are dropped server-side"),
326
+ resource_key: z.string().optional().describe("Optional folder/resource the action targets (enforced against corpus scope)"),
327
+ },
328
+ }, async ({ capability, params, resource_key }) => {
329
+ if (!handle) {
330
+ return {
331
+ content: [{ type: "text", text: "Error: could not derive agent handle from grant token." }],
332
+ isError: true,
333
+ };
334
+ }
335
+ const resp = await apiPost(baseUrl, grantToken, `/api/agents/${encodeURIComponent(handle)}/use`, {
336
+ capability,
337
+ params,
338
+ resource_key,
339
+ });
340
+ return formatResult(resp);
341
+ });
342
+ }
343
+ // ---------------------------------------------------------------------------
344
+ // Server setup
345
+ // ---------------------------------------------------------------------------
346
+ async function main() {
347
+ const { apiKey, grantToken, baseUrl } = parseArgs(process.argv.slice(2));
348
+ const mode = grantToken ? "agent" : "admin";
349
+ const server = new McpServer({
350
+ name: mode === "agent" ? "NoData (agent-scoped)" : "NoData",
351
+ version: "0.4.0",
352
+ });
353
+ if (apiKey)
354
+ registerAdminTools(server, baseUrl, apiKey);
355
+ if (grantToken)
356
+ registerAgentTool(server, baseUrl, grantToken);
357
+ const transport = new StdioServerTransport();
358
+ await server.connect(transport);
359
+ }
360
+ main().catch((err) => {
361
+ console.error("Fatal error:", err);
362
+ process.exit(1);
363
+ });
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@nodatachat/mcp",
3
+ "version": "0.4.0",
4
+ "description": "NoData MCP Server — govern data for AI agents: decide (may I?) + retrieve (authorized context) + read + secret-blind invoke, plus admin governance/encrypt/decrypt/deliver",
5
+ "bin": {
6
+ "nodata-mcp": "./dist/index.js"
7
+ },
8
+ "type": "module",
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsx src/index.ts"
12
+ },
13
+ "dependencies": {
14
+ "@modelcontextprotocol/sdk": "^1.29.0",
15
+ "zod": "^4.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^22.0.0",
19
+ "tsx": "^4.0.0",
20
+ "typescript": "^5.7.0"
21
+ },
22
+ "engines": {
23
+ "node": ">=18.0.0"
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "homepage": "https://www.nodatacapsule.com/developers",
29
+ "keywords": [
30
+ "mcp",
31
+ "model-context-protocol",
32
+ "ai-agents",
33
+ "data-governance",
34
+ "nodata",
35
+ "access-control"
36
+ ],
37
+ "license": "MIT"
38
+ }