@provenedge/realize-mcp 0.1.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/dist/index.js +434 -0
  3. package/package.json +42 -0
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # @provenedge/realize-mcp
2
+
3
+ MCP server for the [ProvenEdge](https://provenedge.com) Realization Engine. Connects AI agents (Claude Code, Cursor, Windsurf, etc.) to your entities, records, views, relationships, and documents via the [Model Context Protocol](https://modelcontextprotocol.io).
4
+
5
+ ## Quick Start
6
+
7
+ No install needed — just configure your AI client:
8
+
9
+ **Claude Code** (`.mcp.json` in project root or `~/.claude/.mcp.json` globally):
10
+
11
+ ```json
12
+ {
13
+ "mcpServers": {
14
+ "provenedge": {
15
+ "command": "npx",
16
+ "args": ["-y", "@provenedge/realize-mcp"],
17
+ "env": {
18
+ "SCAFFOLD_API_URL": "https://api.provenedge.com",
19
+ "SCAFFOLD_API_KEY": "pe_live_..."
20
+ }
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ Then restart your AI agent to pick up the new server.
27
+
28
+ ## Getting an API Key
29
+
30
+ 1. Log in to your ProvenEdge workspace
31
+ 2. Go to **Settings > API Keys**
32
+ 3. Create a key with **READ** or **READ_WRITE** scope
33
+ 4. Copy the key (it's only shown once)
34
+
35
+ ## Environment Variables
36
+
37
+ | Variable | Required | Description |
38
+ |---|---|---|
39
+ | `SCAFFOLD_API_KEY` | Yes | Your API key (`pe_live_...`) |
40
+ | `SCAFFOLD_API_URL` | No | API base URL (defaults to `http://localhost:4000`) |
41
+
42
+ ## Available Tools
43
+
44
+ 27 tools across these categories:
45
+
46
+ | Category | Tools | Scope |
47
+ |---|---|---|
48
+ | **Entities** | list, get, create, update, delete, stats | READ / READ_WRITE |
49
+ | **Fields** | list, add, update, delete | READ / READ_WRITE |
50
+ | **Records** | list, get, create, update, delete, search | READ / READ_WRITE |
51
+ | **Views** | list, get, create, execute, delete | READ / READ_WRITE |
52
+ | **Relationships** | list, create, get_related, link, unlink | READ / READ_WRITE |
53
+ | **Notes** | list, add | READ / READ_WRITE |
54
+ | **Import** | csv | READ_WRITE |
55
+ | **Audit** | get_audit_log | READ |
56
+ | **Profile** | get_profile, get_storage_usage | READ |
57
+ | **Documents** | list_documents | READ |
58
+
59
+ ## Supported Clients
60
+
61
+ | Client | Config Location |
62
+ |---|---|
63
+ | **Claude Code** | `.mcp.json` in project root or `~/.claude/.mcp.json` |
64
+ | **Cursor** | Settings > MCP Servers or `~/.cursor/mcp.json` |
65
+ | **Windsurf** | Settings > MCP |
66
+ | **Continue** | `~/.continue/config.json` under `mcpServers` |
67
+ | **Cline** | Settings > MCP Servers |
68
+
69
+ ## Pin a Version
70
+
71
+ ```json
72
+ "args": ["-y", "@provenedge/realize-mcp@0.1.0"]
73
+ ```
74
+
75
+ ## Security
76
+
77
+ - API keys authenticate as USER role (never elevated, even if the user has ADMIN/SUPERADMIN)
78
+ - READ keys can only read data; READ_WRITE keys can modify data
79
+ - Keys can be revoked instantly from the web UI
80
+ - Rate limited: 100 requests/minute per key
81
+
82
+ ## License
83
+
84
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,434 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+ var API_URL = process.env.SCAFFOLD_API_URL || "http://localhost:4000";
8
+ var API_KEY = process.env.SCAFFOLD_API_KEY;
9
+ if (!API_KEY) {
10
+ process.stderr.write(
11
+ "Error: SCAFFOLD_API_KEY is required. Create one at your Scaffold web UI under API Keys.\n"
12
+ );
13
+ process.exit(1);
14
+ }
15
+ async function api(method, path, opts) {
16
+ const url = new URL(path, API_URL);
17
+ if (opts?.query) {
18
+ for (const [k, v] of Object.entries(opts.query)) {
19
+ if (v !== void 0 && v !== "") url.searchParams.set(k, v);
20
+ }
21
+ }
22
+ try {
23
+ const res = await fetch(url.toString(), {
24
+ method,
25
+ headers: {
26
+ Authorization: `Bearer ${API_KEY}`,
27
+ ...opts?.body ? { "Content-Type": "application/json" } : {}
28
+ },
29
+ body: opts?.body ? JSON.stringify(opts.body) : void 0
30
+ });
31
+ const raw = await res.text();
32
+ let data;
33
+ try {
34
+ data = JSON.parse(raw);
35
+ } catch {
36
+ data = raw;
37
+ }
38
+ if (!res.ok) {
39
+ const msg = typeof data === "object" && data !== null && "error" in data ? String(data.error) : raw;
40
+ return { ok: false, error: msg, status: res.status };
41
+ }
42
+ return { ok: true, data };
43
+ } catch (e) {
44
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
45
+ }
46
+ }
47
+ function success(data) {
48
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
49
+ }
50
+ function error(message) {
51
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
52
+ }
53
+ function respond(result) {
54
+ return result.ok ? success(result.data) : error(result.error);
55
+ }
56
+ var server = new McpServer({
57
+ name: "provenedge-scaffold",
58
+ version: "1.0.0"
59
+ });
60
+ server.tool(
61
+ "list_entities",
62
+ "List all entity types in the workspace. Returns names, slugs, field counts, and descriptions.",
63
+ {},
64
+ async () => respond(await api("GET", "/entities"))
65
+ );
66
+ server.tool(
67
+ "get_entity",
68
+ "Get the full schema for an entity type including every field definition, table settings, and form layout.",
69
+ { slug: z.string().describe("Entity slug (URL-safe identifier, e.g. 'contacts')") },
70
+ async ({ slug }) => respond(await api("GET", `/entities/${slug}`))
71
+ );
72
+ server.tool(
73
+ "create_entity",
74
+ "Create a new entity type with optional initial fields. Returns the created entity definition.",
75
+ {
76
+ name: z.string().describe("Entity name (alphanumeric + underscores only, e.g. 'contacts')"),
77
+ label: z.string().describe("Display label (e.g. 'Contacts')"),
78
+ description: z.string().optional().describe("Entity description"),
79
+ icon: z.string().optional().describe("Tabler icon name (e.g. 'IconUsers')"),
80
+ iconColor: z.string().optional().describe("Icon color hex (e.g. '#4c6ef5')"),
81
+ fields: z.record(z.record(z.unknown())).optional().describe(
82
+ "Initial fields as { fieldName: { label, fieldType, required?, ... } }. fieldType: text, longText, number, email, phone, url, currency, singleSelect, multiSelect, checkbox, date, time, address, location"
83
+ )
84
+ },
85
+ async ({ name, label, description, icon, iconColor, fields }) => {
86
+ const body = { name, label };
87
+ if (description) body.description = description;
88
+ if (icon) body.icon = icon;
89
+ if (iconColor) body.iconColor = iconColor;
90
+ if (fields) body.fields = fields;
91
+ return respond(await api("POST", "/entities", { body }));
92
+ }
93
+ );
94
+ server.tool(
95
+ "update_entity",
96
+ "Update an entity's metadata (label, icon, description, enabled state).",
97
+ {
98
+ slug: z.string().describe("Entity slug"),
99
+ label: z.string().optional(),
100
+ icon: z.string().optional(),
101
+ iconColor: z.string().optional(),
102
+ description: z.string().optional(),
103
+ enabled: z.boolean().optional(),
104
+ priority: z.number().optional().describe("Display order priority")
105
+ },
106
+ async ({ slug, ...updates }) => {
107
+ const body = Object.fromEntries(Object.entries(updates).filter(([, v]) => v !== void 0));
108
+ return respond(await api("PATCH", `/entities/${slug}`, { body }));
109
+ }
110
+ );
111
+ server.tool(
112
+ "delete_entity",
113
+ "Delete an entity type and all its records. This is destructive and cannot be undone.",
114
+ { slug: z.string().describe("Entity slug") },
115
+ async ({ slug }) => respond(await api("DELETE", `/entities/${slug}`))
116
+ );
117
+ server.tool(
118
+ "get_entity_stats",
119
+ "Get record and relationship counts for an entity.",
120
+ { slug: z.string().describe("Entity slug") },
121
+ async ({ slug }) => respond(await api("GET", `/entities/${slug}/stats`))
122
+ );
123
+ server.tool(
124
+ "list_fields",
125
+ "List all fields defined on an entity schema.",
126
+ { entitySlug: z.string().describe("Entity slug") },
127
+ async ({ entitySlug }) => respond(await api("GET", `/entities/${entitySlug}/fields`))
128
+ );
129
+ server.tool(
130
+ "add_field",
131
+ "Add a new field to an entity schema.",
132
+ {
133
+ entitySlug: z.string().describe("Entity slug"),
134
+ name: z.string().describe("Field name (alphanumeric + underscores, e.g. 'email_address')"),
135
+ definition: z.record(z.unknown()).describe(
136
+ "Field definition object. Required keys: 'label' (string), 'fieldType' (one of: text, longText, number, email, phone, url, currency, singleSelect, multiSelect, checkbox, date, time, address, location). Optional: required (bool), unique (bool), default (any), options (array of {value, label, color} for select fields), maxLength/minLength (number), minimum/maximum (number), currency: {code, symbol, decimalPlaces, displayCommas}"
137
+ )
138
+ },
139
+ async ({ entitySlug, name, definition }) => respond(await api("POST", `/entities/${entitySlug}/fields`, { body: { name, definition } }))
140
+ );
141
+ server.tool(
142
+ "update_field",
143
+ "Update a field definition on an entity (label, required, options, etc.).",
144
+ {
145
+ entitySlug: z.string().describe("Entity slug"),
146
+ fieldName: z.string().describe("Field name to update"),
147
+ definition: z.record(z.unknown()).describe("Partial field definition with properties to update")
148
+ },
149
+ async ({ entitySlug, fieldName, definition }) => respond(await api("PATCH", `/entities/${entitySlug}/fields/${fieldName}`, { body: { definition } }))
150
+ );
151
+ server.tool(
152
+ "delete_field",
153
+ "Remove a field from an entity schema. Records keep existing data but the field is hidden.",
154
+ {
155
+ entitySlug: z.string().describe("Entity slug"),
156
+ fieldName: z.string().describe("Field name to delete")
157
+ },
158
+ async ({ entitySlug, fieldName }) => respond(await api("DELETE", `/entities/${entitySlug}/fields/${fieldName}`))
159
+ );
160
+ server.tool(
161
+ "list_records",
162
+ "List records for an entity with optional filtering, sorting, search, and pagination.",
163
+ {
164
+ entitySlug: z.string().describe("Entity slug"),
165
+ page: z.number().optional().describe("Page number (default 1)"),
166
+ pageSize: z.number().optional().describe("Records per page, 1-200 (default 25)"),
167
+ sort: z.string().optional().describe("Field name to sort by"),
168
+ sortDir: z.enum(["asc", "desc"]).optional().describe("Sort direction (default 'asc')"),
169
+ search: z.string().optional().describe("Full-text search query"),
170
+ filter: z.string().optional().describe(
171
+ 'JSON filter object with MongoDB-style operators. Example: {"status": {"$eq": "active"}, "amount": {"$gt": "100"}}. Operators: $eq, $ne, $contains, $gt, $lt, $gte, $lte'
172
+ ),
173
+ fields: z.string().optional().describe("Comma-separated field names to include in response")
174
+ },
175
+ async ({ entitySlug, page, pageSize, sort, sortDir, search, filter, fields }) => respond(
176
+ await api("GET", `/entities/${entitySlug}/records`, {
177
+ query: {
178
+ page: page?.toString(),
179
+ pageSize: pageSize?.toString(),
180
+ sort,
181
+ sortDir,
182
+ search,
183
+ filter,
184
+ fields
185
+ }
186
+ })
187
+ )
188
+ );
189
+ server.tool(
190
+ "get_record",
191
+ "Get a single record by ID with all field data.",
192
+ {
193
+ entitySlug: z.string().describe("Entity slug"),
194
+ recordId: z.string().describe("Record ID")
195
+ },
196
+ async ({ entitySlug, recordId }) => respond(await api("GET", `/entities/${entitySlug}/records/${recordId}`))
197
+ );
198
+ server.tool(
199
+ "create_record",
200
+ "Create a new record for an entity. Pass field values as key-value pairs in data.",
201
+ {
202
+ entitySlug: z.string().describe("Entity slug"),
203
+ data: z.record(z.unknown()).describe("Record data as { fieldName: value }. Field names must match the entity schema.")
204
+ },
205
+ async ({ entitySlug, data }) => respond(await api("POST", `/entities/${entitySlug}/records`, { body: { data } }))
206
+ );
207
+ server.tool(
208
+ "update_record",
209
+ "Update an existing record. Only include fields you want to change.",
210
+ {
211
+ entitySlug: z.string().describe("Entity slug"),
212
+ recordId: z.string().describe("Record ID"),
213
+ data: z.record(z.unknown()).describe("Partial record data with fields to update")
214
+ },
215
+ async ({ entitySlug, recordId, data }) => respond(await api("PATCH", `/entities/${entitySlug}/records/${recordId}`, { body: { data } }))
216
+ );
217
+ server.tool(
218
+ "delete_record",
219
+ "Delete a single record by ID.",
220
+ {
221
+ entitySlug: z.string().describe("Entity slug"),
222
+ recordId: z.string().describe("Record ID")
223
+ },
224
+ async ({ entitySlug, recordId }) => respond(await api("DELETE", `/entities/${entitySlug}/records/${recordId}`))
225
+ );
226
+ server.tool(
227
+ "search_records",
228
+ "Full-text search across all fields of an entity.",
229
+ {
230
+ entitySlug: z.string().describe("Entity slug"),
231
+ query: z.string().describe("Search query text")
232
+ },
233
+ async ({ entitySlug, query }) => respond(await api("GET", `/entities/${entitySlug}/records/search`, { query: { q: query } }))
234
+ );
235
+ server.tool(
236
+ "list_views",
237
+ "List all saved views the current user has access to.",
238
+ {},
239
+ async () => respond(await api("GET", "/views"))
240
+ );
241
+ server.tool(
242
+ "get_view",
243
+ "Get a saved view definition including its field selection, filters, sort, and parameters.",
244
+ { viewId: z.string().describe("View ID") },
245
+ async ({ viewId }) => respond(await api("GET", `/views/${viewId}`))
246
+ );
247
+ server.tool(
248
+ "create_view",
249
+ "Create a saved view with field selection, filters, sorting, and optional parameters.",
250
+ {
251
+ name: z.string().describe("View name"),
252
+ entityId: z.string().describe("Base entity ID (not slug \u2014 get from list_entities)"),
253
+ description: z.string().optional(),
254
+ icon: z.string().optional(),
255
+ isShared: z.boolean().optional().describe("Whether other users can see this view"),
256
+ config: z.record(z.unknown()).describe(
257
+ "View configuration object with: baseEntityId (string), fields (array of {entityId, fieldName, label, relationshipId?, subField?}), filters? (array of {fieldName, operator, value, entityId?}), sort? ({field, direction: 'asc'|'desc'}), parameters? (array of {name, label, fieldType, required?, defaultValue?})"
258
+ )
259
+ },
260
+ async ({ name, entityId, description, icon, isShared, config }) => {
261
+ const body = { name, entityId, config };
262
+ if (description) body.description = description;
263
+ if (icon) body.icon = icon;
264
+ if (isShared !== void 0) body.isShared = isShared;
265
+ return respond(await api("POST", "/views", { body }));
266
+ }
267
+ );
268
+ server.tool(
269
+ "execute_view",
270
+ "Execute a saved view and return the matching records with pagination.",
271
+ {
272
+ viewId: z.string().describe("View ID"),
273
+ page: z.number().optional().describe("Page number (default 1)"),
274
+ pageSize: z.number().optional().describe("Records per page (default 25)"),
275
+ viewParams: z.string().optional().describe("JSON object of parameter name \u2192 value for parameterized views")
276
+ },
277
+ async ({ viewId, page, pageSize, viewParams }) => respond(
278
+ await api("GET", `/views/${viewId}/execute`, {
279
+ query: {
280
+ page: page?.toString(),
281
+ pageSize: pageSize?.toString(),
282
+ viewParams
283
+ }
284
+ })
285
+ )
286
+ );
287
+ server.tool(
288
+ "delete_view",
289
+ "Delete a saved view.",
290
+ { viewId: z.string().describe("View ID") },
291
+ async ({ viewId }) => respond(await api("DELETE", `/views/${viewId}`))
292
+ );
293
+ server.tool(
294
+ "list_relationships",
295
+ "List all relationships for an entity (both as source and target).",
296
+ { entitySlug: z.string().describe("Entity slug") },
297
+ async ({ entitySlug }) => respond(await api("GET", `/entities/${entitySlug}/relationships`))
298
+ );
299
+ server.tool(
300
+ "create_relationship",
301
+ "Create a relationship between two entity types.",
302
+ {
303
+ entitySlug: z.string().describe("Source entity slug"),
304
+ targetEntityId: z.string().describe("Target entity ID"),
305
+ name: z.string().describe("Relationship name (alphanumeric)"),
306
+ label: z.string().describe("Display label (e.g. 'Assigned To')"),
307
+ reverseLabel: z.string().describe("Reverse label (e.g. 'Assigned Contacts')"),
308
+ cardinality: z.enum(["ONE_TO_MANY", "MANY_TO_MANY"]).describe("Relationship cardinality")
309
+ },
310
+ async ({ entitySlug, ...body }) => respond(await api("POST", `/entities/${entitySlug}/relationships`, { body }))
311
+ );
312
+ server.tool(
313
+ "get_related_records",
314
+ "Get records linked to a specific record through a relationship.",
315
+ {
316
+ entitySlug: z.string().describe("Entity slug"),
317
+ recordId: z.string().describe("Source record ID"),
318
+ relationshipId: z.string().describe("Relationship ID")
319
+ },
320
+ async ({ entitySlug, recordId, relationshipId }) => respond(await api("GET", `/entities/${entitySlug}/records/${recordId}/related/${relationshipId}`))
321
+ );
322
+ server.tool(
323
+ "link_records",
324
+ "Link one or more records to a source record through a relationship.",
325
+ {
326
+ entitySlug: z.string().describe("Source entity slug"),
327
+ recordId: z.string().describe("Source record ID"),
328
+ relationshipId: z.string().describe("Relationship ID"),
329
+ targetRecordIds: z.array(z.string()).describe("Array of target record IDs to link")
330
+ },
331
+ async ({ entitySlug, recordId, relationshipId, targetRecordIds }) => respond(
332
+ await api("POST", `/entities/${entitySlug}/records/${recordId}/related/${relationshipId}`, {
333
+ body: { targetRecordIds }
334
+ })
335
+ )
336
+ );
337
+ server.tool(
338
+ "unlink_records",
339
+ "Remove a link between two records in a relationship.",
340
+ {
341
+ entitySlug: z.string().describe("Source entity slug"),
342
+ recordId: z.string().describe("Source record ID"),
343
+ relationshipId: z.string().describe("Relationship ID"),
344
+ targetRecordId: z.string().describe("Target record ID to unlink")
345
+ },
346
+ async ({ entitySlug, recordId, relationshipId, targetRecordId }) => respond(
347
+ await api(
348
+ "DELETE",
349
+ `/entities/${entitySlug}/records/${recordId}/related/${relationshipId}/${targetRecordId}`
350
+ )
351
+ )
352
+ );
353
+ server.tool(
354
+ "list_notes",
355
+ "List all notes on a record, newest first.",
356
+ {
357
+ entitySlug: z.string().describe("Entity slug"),
358
+ recordId: z.string().describe("Record ID")
359
+ },
360
+ async ({ entitySlug, recordId }) => respond(await api("GET", `/entities/${entitySlug}/records/${recordId}/notes`))
361
+ );
362
+ server.tool(
363
+ "add_note",
364
+ "Add a text note to a record.",
365
+ {
366
+ entitySlug: z.string().describe("Entity slug"),
367
+ recordId: z.string().describe("Record ID"),
368
+ content: z.string().min(1).max(1e4).describe("Note text content")
369
+ },
370
+ async ({ entitySlug, recordId, content }) => respond(await api("POST", `/entities/${entitySlug}/records/${recordId}/notes`, { body: { content } }))
371
+ );
372
+ server.tool(
373
+ "import_csv",
374
+ "Import records from CSV data into an entity. Optionally creates the entity if it doesn't exist.",
375
+ {
376
+ entitySlug: z.string().describe("Target entity slug"),
377
+ csvContent: z.string().describe("Raw CSV content (headers in first row)"),
378
+ delimiter: z.string().optional().describe("Column delimiter (default ',')"),
379
+ createIfMissing: z.boolean().optional().describe("Auto-create entity from CSV headers if it doesn't exist")
380
+ },
381
+ async ({ entitySlug, csvContent, delimiter, createIfMissing }) => {
382
+ const body = { entitySlug, csvContent };
383
+ if (delimiter) body.delimiter = delimiter;
384
+ if (createIfMissing !== void 0) body.createIfMissing = createIfMissing;
385
+ return respond(await api("POST", "/import/csv", { body }));
386
+ }
387
+ );
388
+ server.tool(
389
+ "get_audit_log",
390
+ "Get the audit log for an entity showing create, update, and delete history.",
391
+ {
392
+ entitySlug: z.string().describe("Entity slug"),
393
+ recordId: z.string().optional().describe("Optional record ID to filter to a specific record"),
394
+ page: z.number().optional(),
395
+ pageSize: z.number().optional()
396
+ },
397
+ async ({ entitySlug, recordId, page, pageSize }) => {
398
+ const path = recordId ? `/entities/${entitySlug}/records/${recordId}/audit` : `/entities/${entitySlug}/audit`;
399
+ return respond(
400
+ await api("GET", path, {
401
+ query: { page: page?.toString(), pageSize: pageSize?.toString() }
402
+ })
403
+ );
404
+ }
405
+ );
406
+ server.tool(
407
+ "get_profile",
408
+ "Get the current user's profile (name, email, role, storage usage).",
409
+ {},
410
+ async () => respond(await api("GET", "/profile"))
411
+ );
412
+ server.tool(
413
+ "get_storage_usage",
414
+ "Get the current user's file storage usage and limits.",
415
+ {},
416
+ async () => respond(await api("GET", "/profile/storage"))
417
+ );
418
+ server.tool(
419
+ "list_documents",
420
+ "List all documents uploaded by the current user.",
421
+ {},
422
+ async () => respond(await api("GET", "/documents"))
423
+ );
424
+ async function main() {
425
+ const transport = new StdioServerTransport();
426
+ await server.connect(transport);
427
+ process.stderr.write(`ProvenEdge MCP server running \u2014 API: ${API_URL}
428
+ `);
429
+ }
430
+ main().catch((e) => {
431
+ process.stderr.write(`Fatal: ${e}
432
+ `);
433
+ process.exit(1);
434
+ });
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@provenedge/realize-mcp",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "private": false,
6
+ "description": "MCP server for ProvenEdge Realization Engine — connect AI agents to your entities, records, views, and documents",
7
+ "keywords": [
8
+ "mcp",
9
+ "model-context-protocol",
10
+ "provenedge",
11
+ "realization-engine",
12
+ "ai",
13
+ "claude",
14
+ "cursor"
15
+ ],
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/provenedge/app-scaffold.git",
20
+ "directory": "packages/mcp-server"
21
+ },
22
+ "homepage": "https://provenedge.com",
23
+ "bin": {
24
+ "realize-mcp": "./dist/index.js"
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsup",
31
+ "dev": "tsup --watch",
32
+ "prepublishOnly": "npm run build"
33
+ },
34
+ "dependencies": {
35
+ "@modelcontextprotocol/sdk": "^1.12.0",
36
+ "zod": "^3.24.4"
37
+ },
38
+ "devDependencies": {
39
+ "tsup": "^8.4.0",
40
+ "typescript": "^5.8.3"
41
+ }
42
+ }