@socialneuron/mcp-server 1.6.0 → 1.6.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/CHANGELOG.md CHANGED
@@ -2,6 +2,45 @@
2
2
 
3
3
  All notable changes to `@socialneuron/mcp-server` will be documented in this file.
4
4
 
5
+ ## [1.6.1] - 2026-03-22
6
+
7
+ ### Security
8
+ - **Explicit body size limit**: `express.json({ limit: '50kb' })` prevents DoS via oversized payloads.
9
+ - **Error message sanitization**: MCP POST catch block now uses `sanitizeError()` — no more internal paths or table names in error responses.
10
+ - **PII removal**: Removed `email` from API key validation chain (7 files). Key validation no longer exposes user email addresses.
11
+ - **Generation rate limiting**: Added explicit `generation` category at 20 req/min (previously fell back to `read` at 60/min).
12
+ - **npm provenance**: Added `--provenance` flag and `id-token: write` permission to release workflow for supply chain verification.
13
+ - **Security comment**: Documented that Edge Functions must not trust `x-internal-worker-call` header without Bearer token verification.
14
+
15
+ ### Fixed
16
+ - **hono prototype pollution**: Updated transitive dependency to fix GHSA-v8w9-8mx6-g223.
17
+ - `npm audit` now reports 0 vulnerabilities.
18
+
19
+ ### Added
20
+ - 18 examples (8 REST curl, 5 TypeScript SDK, 4 CLI, 1 MCP prompts).
21
+ - TypeScript SDK package (`packages/sdk/`) with 9 resource classes.
22
+ - CLI tab completion and content generation commands.
23
+ - SDK documentation and release workflow.
24
+
25
+ ## [1.6.0] - 2026-03-21
26
+
27
+ ### Added
28
+ - **REST API layer**: Universal tool proxy at `POST /v1/tools/:name` — call any of the 52 MCP tools via standard HTTP REST. No MCP client required.
29
+ - **OpenAPI 3.1 spec**: Auto-generated from TOOL_CATALOG at `/openapi.json` — always in sync with tools.
30
+ - **15 convenience endpoints**: Resource-oriented routes for common operations (`/v1/credits`, `/v1/content/generate`, `/v1/posts`, etc.).
31
+ - **Express HTTP transport**: New `dist/http.js` entry point for running as a standalone REST API server.
32
+ - **MCP Registry metadata**: `server.json` with mcpName, endpoints, env, and auth configuration for registry discovery.
33
+ - **Cursor Directory manifest**: Plugin manifest for Cursor IDE integration.
34
+
35
+ ### Fixed
36
+ - **TS2345**: Cast Express route param to string for strict TypeScript compatibility.
37
+ - **npm publish 404**: Removed `--provenance` flag from release workflow (incompatible with scoped packages on granular tokens).
38
+
39
+ ### Changed
40
+ - Dual transport support: MCP (stdio) and HTTP (Express) from a single codebase.
41
+ - SECURITY.md updated with v1.6.x in supported versions.
42
+ - `docs/auth.md` domain reference corrected (`www.socialneuron.com` → `socialneuron.com`).
43
+
5
44
  ## [1.5.2] - 2026-03-20
6
45
 
7
46
  ### Added
package/dist/http.js CHANGED
@@ -343,7 +343,6 @@ __export(supabase_exports, {
343
343
  CLOUD_SUPABASE_URL: () => CLOUD_SUPABASE_URL,
344
344
  getAuthMode: () => getAuthMode,
345
345
  getAuthenticatedApiKey: () => getAuthenticatedApiKey,
346
- getAuthenticatedEmail: () => getAuthenticatedEmail,
347
346
  getAuthenticatedExpiresAt: () => getAuthenticatedExpiresAt,
348
347
  getAuthenticatedScopes: () => getAuthenticatedScopes,
349
348
  getDefaultProjectId: () => getDefaultProjectId,
@@ -431,7 +430,6 @@ async function initializeAuth() {
431
430
  _authMode = "api-key";
432
431
  authenticatedUserId = result.userId;
433
432
  authenticatedScopes = result.scopes && result.scopes.length > 0 ? result.scopes : ["mcp:read"];
434
- authenticatedEmail = result.email || null;
435
433
  authenticatedExpiresAt = result.expiresAt || null;
436
434
  console.error(
437
435
  "[MCP] Authenticated via API key (prefix: " + apiKey.substring(0, 6) + "..." + apiKey.slice(-4) + ")"
@@ -487,9 +485,6 @@ function getMcpRunId() {
487
485
  function getAuthenticatedScopes() {
488
486
  return authenticatedScopes;
489
487
  }
490
- function getAuthenticatedEmail() {
491
- return authenticatedEmail;
492
- }
493
488
  function getAuthenticatedExpiresAt() {
494
489
  return authenticatedExpiresAt;
495
490
  }
@@ -528,7 +523,7 @@ async function logMcpToolInvocation(args) {
528
523
  captureToolEvent(args).catch(() => {
529
524
  });
530
525
  }
531
- var SUPABASE_URL, SUPABASE_SERVICE_KEY, client2, _authMode, authenticatedUserId, authenticatedScopes, authenticatedEmail, authenticatedExpiresAt, authenticatedApiKey, MCP_RUN_ID, CLOUD_SUPABASE_URL, CLOUD_SUPABASE_ANON_KEY, projectIdCache;
526
+ var SUPABASE_URL, SUPABASE_SERVICE_KEY, client2, _authMode, authenticatedUserId, authenticatedScopes, authenticatedExpiresAt, authenticatedApiKey, MCP_RUN_ID, CLOUD_SUPABASE_URL, CLOUD_SUPABASE_ANON_KEY, projectIdCache;
532
527
  var init_supabase = __esm({
533
528
  "src/lib/supabase.ts"() {
534
529
  "use strict";
@@ -540,7 +535,6 @@ var init_supabase = __esm({
540
535
  _authMode = "service-role";
541
536
  authenticatedUserId = null;
542
537
  authenticatedScopes = [];
543
- authenticatedEmail = null;
544
538
  authenticatedExpiresAt = null;
545
539
  authenticatedApiKey = null;
546
540
  MCP_RUN_ID = randomUUID();
@@ -787,6 +781,8 @@ async function callEdgeFunction(functionName, body, options) {
787
781
  var CATEGORY_CONFIGS = {
788
782
  posting: { maxTokens: 30, refillRate: 30 / 60 },
789
783
  // 30 req/min
784
+ generation: { maxTokens: 20, refillRate: 20 / 60 },
785
+ // 20 req/min — AI content generation (mcp:write)
790
786
  screenshot: { maxTokens: 10, refillRate: 10 / 60 },
791
787
  // 10 req/min
792
788
  read: { maxTokens: 60, refillRate: 60 / 60 }
@@ -1363,6 +1359,18 @@ function sanitizeDbError(error) {
1363
1359
  }
1364
1360
  return "Database operation failed. Please try again.";
1365
1361
  }
1362
+ function sanitizeError(error) {
1363
+ const msg = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
1364
+ if (process.env.NODE_ENV !== "production") {
1365
+ console.error("[Error]", msg);
1366
+ }
1367
+ for (const [pattern, userMessage] of ERROR_PATTERNS) {
1368
+ if (pattern.test(msg)) {
1369
+ return userMessage;
1370
+ }
1371
+ }
1372
+ return "An unexpected error occurred. Please try again.";
1373
+ }
1366
1374
 
1367
1375
  // src/tools/content.ts
1368
1376
  init_request_context();
@@ -9449,7 +9457,7 @@ async function verifyApiKey(apiKey, supabaseUrl, supabaseAnonKey) {
9449
9457
  clientId: "api-key",
9450
9458
  scopes: data.scopes ?? ["mcp:read"],
9451
9459
  expiresAt,
9452
- extra: { userId: data.userId, email: data.email }
9460
+ extra: { userId: data.userId }
9453
9461
  };
9454
9462
  } catch (err) {
9455
9463
  clearTimeout(timer);
@@ -10521,7 +10529,7 @@ var cleanupInterval = setInterval(
10521
10529
  );
10522
10530
  var app = express();
10523
10531
  app.disable("x-powered-by");
10524
- app.use(express.json());
10532
+ app.use(express.json({ limit: "50kb" }));
10525
10533
  app.set("trust proxy", 1);
10526
10534
  var ipBuckets = /* @__PURE__ */ new Map();
10527
10535
  var IP_RATE_MAX = 60;
@@ -10626,7 +10634,7 @@ async function authenticateRequest(req, res, next) {
10626
10634
  };
10627
10635
  next();
10628
10636
  } catch (err) {
10629
- const message = err instanceof Error ? err.message : "Token verification failed";
10637
+ const message = sanitizeError(err);
10630
10638
  res.status(401).json({
10631
10639
  error: "invalid_token",
10632
10640
  error_description: message
@@ -10737,9 +10745,10 @@ app.post(
10737
10745
  () => transport.handleRequest(req, res, req.body)
10738
10746
  );
10739
10747
  } catch (err) {
10740
- const message = err instanceof Error ? err.message : "Internal server error";
10741
- console.error(`[MCP HTTP] POST /mcp error: ${message}`);
10748
+ const rawMessage = err instanceof Error ? err.message : "Internal server error";
10749
+ console.error(`[MCP HTTP] POST /mcp error: ${rawMessage}`);
10742
10750
  if (!res.headersSent) {
10751
+ const message = sanitizeError(err);
10743
10752
  res.status(500).json({ jsonrpc: "2.0", error: { code: -32603, message } });
10744
10753
  }
10745
10754
  }
package/dist/index.js CHANGED
@@ -350,7 +350,6 @@ __export(supabase_exports, {
350
350
  CLOUD_SUPABASE_URL: () => CLOUD_SUPABASE_URL,
351
351
  getAuthMode: () => getAuthMode,
352
352
  getAuthenticatedApiKey: () => getAuthenticatedApiKey,
353
- getAuthenticatedEmail: () => getAuthenticatedEmail,
354
353
  getAuthenticatedExpiresAt: () => getAuthenticatedExpiresAt,
355
354
  getAuthenticatedScopes: () => getAuthenticatedScopes,
356
355
  getDefaultProjectId: () => getDefaultProjectId,
@@ -438,7 +437,6 @@ async function initializeAuth() {
438
437
  _authMode = "api-key";
439
438
  authenticatedUserId = result.userId;
440
439
  authenticatedScopes = result.scopes && result.scopes.length > 0 ? result.scopes : ["mcp:read"];
441
- authenticatedEmail = result.email || null;
442
440
  authenticatedExpiresAt = result.expiresAt || null;
443
441
  console.error(
444
442
  "[MCP] Authenticated via API key (prefix: " + apiKey.substring(0, 6) + "..." + apiKey.slice(-4) + ")"
@@ -494,9 +492,6 @@ function getMcpRunId() {
494
492
  function getAuthenticatedScopes() {
495
493
  return authenticatedScopes;
496
494
  }
497
- function getAuthenticatedEmail() {
498
- return authenticatedEmail;
499
- }
500
495
  function getAuthenticatedExpiresAt() {
501
496
  return authenticatedExpiresAt;
502
497
  }
@@ -535,7 +530,7 @@ async function logMcpToolInvocation(args) {
535
530
  captureToolEvent(args).catch(() => {
536
531
  });
537
532
  }
538
- var SUPABASE_URL, SUPABASE_SERVICE_KEY, client2, _authMode, authenticatedUserId, authenticatedScopes, authenticatedEmail, authenticatedExpiresAt, authenticatedApiKey, MCP_RUN_ID, CLOUD_SUPABASE_URL, CLOUD_SUPABASE_ANON_KEY, projectIdCache;
533
+ var SUPABASE_URL, SUPABASE_SERVICE_KEY, client2, _authMode, authenticatedUserId, authenticatedScopes, authenticatedExpiresAt, authenticatedApiKey, MCP_RUN_ID, CLOUD_SUPABASE_URL, CLOUD_SUPABASE_ANON_KEY, projectIdCache;
539
534
  var init_supabase = __esm({
540
535
  "src/lib/supabase.ts"() {
541
536
  "use strict";
@@ -547,7 +542,6 @@ var init_supabase = __esm({
547
542
  _authMode = "service-role";
548
543
  authenticatedUserId = null;
549
544
  authenticatedScopes = [];
550
- authenticatedEmail = null;
551
545
  authenticatedExpiresAt = null;
552
546
  authenticatedApiKey = null;
553
547
  MCP_RUN_ID = randomUUID();
@@ -2369,7 +2363,6 @@ async function handleInfo(args, asJson) {
2369
2363
  const result = await validateApiKey2(apiKey);
2370
2364
  if (result.valid) {
2371
2365
  info.auth = {
2372
- email: result.email || null,
2373
2366
  scopes: result.scopes || [],
2374
2367
  expiresAt: result.expiresAt || null
2375
2368
  };
@@ -2401,7 +2394,7 @@ async function handleInfo(args, asJson) {
2401
2394
  console.error("Auth: not configured");
2402
2395
  } else if (info.auth) {
2403
2396
  const auth = info.auth;
2404
- console.error(`Auth: ${auth.email ?? "authenticated"}`);
2397
+ console.error("Auth: authenticated");
2405
2398
  console.error(`Scopes: ${auth.scopes.length > 0 ? auth.scopes.join(", ") : "none"}`);
2406
2399
  if (auth.expiresAt) {
2407
2400
  console.error(`Expires: ${auth.expiresAt}`);
@@ -3280,7 +3273,7 @@ async function runLoginPaste() {
3280
3273
  await saveSupabaseUrl(getDefaultSupabaseUrl2());
3281
3274
  console.error("");
3282
3275
  console.error(" API key saved securely.");
3283
- console.error(` User: ${result.email || "unknown"}`);
3276
+ console.error(` User: ${result.userId || "unknown"}`);
3284
3277
  console.error(` Scopes: ${result.scopes?.join(", ") || "mcp:full"}`);
3285
3278
  if (result.expiresAt) {
3286
3279
  const daysLeft = Math.ceil(
@@ -3429,7 +3422,6 @@ async function runWhoami(options) {
3429
3422
  if (asJson) {
3430
3423
  const payload = {
3431
3424
  ok: true,
3432
- email: result.email || null,
3433
3425
  userId: result.userId,
3434
3426
  keyPrefix: apiKey.substring(0, 12) + "...",
3435
3427
  scopes: result.scopes || ["mcp:full"],
@@ -3439,7 +3431,6 @@ async function runWhoami(options) {
3439
3431
  process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
3440
3432
  } else {
3441
3433
  console.error("");
3442
- console.error(` Email: ${result.email || "(not available)"}`);
3443
3434
  console.error(` User ID: ${result.userId}`);
3444
3435
  console.error(` Key: ${apiKey.substring(0, 12)}...`);
3445
3436
  console.error(` Scopes: ${result.scopes?.join(", ") || "mcp:full"}`);
@@ -3482,7 +3473,7 @@ async function runHealthCheck(options) {
3482
3473
  checks.push({
3483
3474
  name: "Key Valid",
3484
3475
  ok: true,
3485
- detail: `User: ${result.email || result.userId}`
3476
+ detail: `User: ${result.userId}`
3486
3477
  });
3487
3478
  checks.push({
3488
3479
  name: "Scopes",
@@ -3595,7 +3586,7 @@ async function runRepl() {
3595
3586
  Social Neuron CLI v${MCP_VERSION} \u2014 Interactive Mode
3596
3587
  `);
3597
3588
  process.stderr.write("Type a command, .help for help, or .exit to quit.\n\n");
3598
- let authEmail = null;
3589
+ let authUserId = null;
3599
3590
  try {
3600
3591
  const { loadApiKey: loadApiKey2 } = await Promise.resolve().then(() => (init_credentials(), credentials_exports));
3601
3592
  const { validateApiKey: validateApiKey2 } = await Promise.resolve().then(() => (init_api_keys(), api_keys_exports));
@@ -3603,8 +3594,8 @@ Social Neuron CLI v${MCP_VERSION} \u2014 Interactive Mode
3603
3594
  if (key) {
3604
3595
  const result = await validateApiKey2(key);
3605
3596
  if (result.valid) {
3606
- authEmail = result.email || null;
3607
- process.stderr.write(` Authenticated as: ${authEmail || "unknown"}
3597
+ authUserId = result.userId || null;
3598
+ process.stderr.write(` Authenticated (user: ${authUserId || "unknown"})
3608
3599
 
3609
3600
  `);
3610
3601
  }
@@ -3638,7 +3629,7 @@ Social Neuron CLI v${MCP_VERSION} \u2014 Interactive Mode
3638
3629
  ".exit",
3639
3630
  ".clear"
3640
3631
  ];
3641
- const promptStr = authEmail ? `sn[${authEmail.split("@")[0]}]> ` : "sn> ";
3632
+ const promptStr = authUserId ? `sn[${authUserId.substring(0, 8)}]> ` : "sn> ";
3642
3633
  const rl = createInterface2({
3643
3634
  input: process.stdin,
3644
3635
  output: process.stderr,
@@ -3829,6 +3820,8 @@ import { z } from "zod";
3829
3820
  var CATEGORY_CONFIGS = {
3830
3821
  posting: { maxTokens: 30, refillRate: 30 / 60 },
3831
3822
  // 30 req/min
3823
+ generation: { maxTokens: 20, refillRate: 20 / 60 },
3824
+ // 20 req/min — AI content generation (mcp:write)
3832
3825
  screenshot: { maxTokens: 10, refillRate: 10 / 60 },
3833
3826
  // 10 req/min
3834
3827
  read: { maxTokens: 60, refillRate: 60 / 60 }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@socialneuron/mcp-server",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "MCP server for Social Neuron - AI content creation platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",