@open-mercato/ai-assistant 0.6.5-develop.5337.1.534b781eac → 0.6.5

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 (82) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +14 -2
  3. package/README.md +1 -1
  4. package/dist/modules/ai_assistant/ai-tools/meta-pack.js +3 -1
  5. package/dist/modules/ai_assistant/ai-tools/meta-pack.js.map +2 -2
  6. package/dist/modules/ai_assistant/api/mcp-key/route.js +95 -0
  7. package/dist/modules/ai_assistant/api/mcp-key/route.js.map +7 -0
  8. package/dist/modules/ai_assistant/cli.js +14 -6
  9. package/dist/modules/ai_assistant/cli.js.map +2 -2
  10. package/dist/modules/ai_assistant/components/McpConfigDialog.js +1 -1
  11. package/dist/modules/ai_assistant/components/McpConfigDialog.js.map +2 -2
  12. package/dist/modules/ai_assistant/data/entities.js +4 -0
  13. package/dist/modules/ai_assistant/data/entities.js.map +2 -2
  14. package/dist/modules/ai_assistant/lib/ai-api-operation-runner.js +3 -1
  15. package/dist/modules/ai_assistant/lib/ai-api-operation-runner.js.map +2 -2
  16. package/dist/modules/ai_assistant/lib/ai-overrides.js.map +1 -1
  17. package/dist/modules/ai_assistant/lib/ai-sdk.js.map +1 -1
  18. package/dist/modules/ai_assistant/lib/attachment-parts.js +10 -3
  19. package/dist/modules/ai_assistant/lib/attachment-parts.js.map +2 -2
  20. package/dist/modules/ai_assistant/lib/chat-config.js.map +1 -1
  21. package/dist/modules/ai_assistant/lib/client-factory.js +0 -8
  22. package/dist/modules/ai_assistant/lib/client-factory.js.map +2 -2
  23. package/dist/modules/ai_assistant/lib/codemode-tools.js +26 -0
  24. package/dist/modules/ai_assistant/lib/codemode-tools.js.map +2 -2
  25. package/dist/modules/ai_assistant/lib/generated-registry-loader.js +23 -3
  26. package/dist/modules/ai_assistant/lib/generated-registry-loader.js.map +2 -2
  27. package/dist/modules/ai_assistant/lib/llm-adapters/anthropic.js.map +1 -1
  28. package/dist/modules/ai_assistant/lib/llm-adapters/google.js.map +1 -1
  29. package/dist/modules/ai_assistant/lib/llm-adapters/openai.js.map +1 -1
  30. package/dist/modules/ai_assistant/lib/llm-bootstrap.js.map +1 -1
  31. package/dist/modules/ai_assistant/lib/mcp-client.js +2 -4
  32. package/dist/modules/ai_assistant/lib/mcp-client.js.map +2 -2
  33. package/dist/modules/ai_assistant/lib/mcp-dev-key-resolution.js +71 -0
  34. package/dist/modules/ai_assistant/lib/mcp-dev-key-resolution.js.map +7 -0
  35. package/dist/modules/ai_assistant/lib/mcp-dev-server.js +1 -30
  36. package/dist/modules/ai_assistant/lib/mcp-dev-server.js.map +2 -2
  37. package/dist/modules/ai_assistant/lib/mcp-server.js +29 -23
  38. package/dist/modules/ai_assistant/lib/mcp-server.js.map +2 -2
  39. package/dist/modules/ai_assistant/lib/openai-compatible-presets.js.map +1 -1
  40. package/dist/modules/ai_assistant/lib/task-plan-stream.js.map +1 -1
  41. package/dist/modules/ai_assistant/setup.js +9 -2
  42. package/dist/modules/ai_assistant/setup.js.map +2 -2
  43. package/package.json +11 -12
  44. package/src/modules/ai_assistant/__tests__/cli-mcp-serve-key.test.ts +63 -0
  45. package/src/modules/ai_assistant/__tests__/setup-schedules.test.ts +33 -0
  46. package/src/modules/ai_assistant/ai-tools/__tests__/meta-pack.test.ts +29 -0
  47. package/src/modules/ai_assistant/ai-tools/meta-pack.ts +7 -1
  48. package/src/modules/ai_assistant/api/mcp-key/__tests__/route.test.ts +144 -0
  49. package/src/modules/ai_assistant/api/mcp-key/route.ts +125 -0
  50. package/src/modules/ai_assistant/cli.ts +18 -5
  51. package/src/modules/ai_assistant/components/McpConfigDialog.tsx +5 -1
  52. package/src/modules/ai_assistant/data/__tests__/schema-unique-indexes.test.ts +39 -0
  53. package/src/modules/ai_assistant/data/entities.ts +5 -0
  54. package/src/modules/ai_assistant/lib/__tests__/ai-overrides.test.ts +1 -1
  55. package/src/modules/ai_assistant/lib/__tests__/attachment-parts.test.ts +112 -0
  56. package/src/modules/ai_assistant/lib/__tests__/client-factory.test.ts +42 -0
  57. package/src/modules/ai_assistant/lib/__tests__/codemode-tools.test.ts +107 -0
  58. package/src/modules/ai_assistant/lib/__tests__/generated-registry-loader.test.ts +30 -3
  59. package/src/modules/ai_assistant/lib/__tests__/mcp-client.test.ts +20 -1
  60. package/src/modules/ai_assistant/lib/__tests__/mcp-dev-key-resolution.test.ts +92 -0
  61. package/src/modules/ai_assistant/lib/__tests__/mcp-server-auth-fail-closed.test.ts +133 -0
  62. package/src/modules/ai_assistant/lib/__tests__/task-plan-stream.test.ts +1 -1
  63. package/src/modules/ai_assistant/lib/ai-api-operation-runner.ts +9 -0
  64. package/src/modules/ai_assistant/lib/ai-overrides.ts +1 -1
  65. package/src/modules/ai_assistant/lib/ai-sdk.ts +1 -1
  66. package/src/modules/ai_assistant/lib/attachment-parts.ts +26 -5
  67. package/src/modules/ai_assistant/lib/chat-config.ts +1 -1
  68. package/src/modules/ai_assistant/lib/client-factory.ts +3 -9
  69. package/src/modules/ai_assistant/lib/codemode-tools.ts +49 -0
  70. package/src/modules/ai_assistant/lib/generated-registry-loader.ts +35 -3
  71. package/src/modules/ai_assistant/lib/llm-adapters/anthropic.ts +1 -1
  72. package/src/modules/ai_assistant/lib/llm-adapters/google.ts +1 -1
  73. package/src/modules/ai_assistant/lib/llm-adapters/openai.ts +1 -1
  74. package/src/modules/ai_assistant/lib/llm-bootstrap.ts +1 -1
  75. package/src/modules/ai_assistant/lib/mcp-client.ts +9 -5
  76. package/src/modules/ai_assistant/lib/mcp-dev-key-resolution.ts +107 -0
  77. package/src/modules/ai_assistant/lib/mcp-dev-server.ts +1 -36
  78. package/src/modules/ai_assistant/lib/mcp-server.ts +47 -33
  79. package/src/modules/ai_assistant/lib/openai-compatible-presets.ts +1 -1
  80. package/src/modules/ai_assistant/lib/task-plan-stream.ts +1 -1
  81. package/src/modules/ai_assistant/lib/types.ts +7 -0
  82. package/src/modules/ai_assistant/setup.ts +11 -2
@@ -1,2 +1,2 @@
1
- [build:ai-assistant] found 213 entry points
1
+ [build:ai-assistant] found 215 entry points
2
2
  [build:ai-assistant] built successfully
package/AGENTS.md CHANGED
@@ -219,7 +219,7 @@ Full reference: `apps/docs/docs/framework/ai-assistant/ui-parts.mdx`.
219
219
 
220
220
  ### How to Override or Extend Another Module's Agent or Tool
221
221
 
222
- Modules can replace/disable any AI agent or AI tool that another module registered, or patch an existing agent by appending, deleting, or replacing allowed tools, system-prompt text, and starter suggestions. Use full overrides when you need to swap the whole behavior; use `aiAgentExtensions` when a downstream module only wants to adjust a shipped agent, such as adding "show catalog stats" while removing an irrelevant starter prompt. See spec `.ai/specs/2026-04-30-ai-overrides-and-module-disable.md` and `apps/docs/docs/framework/ai-assistant/overrides.mdx`.
222
+ Modules can replace/disable any AI agent or AI tool that another module registered, or patch an existing agent by appending, deleting, or replacing allowed tools, system-prompt text, and starter suggestions. Use full overrides when you need to swap the whole behavior; use `aiAgentExtensions` when a downstream module only wants to adjust a shipped agent, such as adding "show catalog stats" while removing an irrelevant starter prompt. See spec `.ai/specs/implemented/2026-04-30-ai-overrides-and-module-disable.md` and `apps/docs/docs/framework/ai-assistant/overrides.mdx`.
223
223
 
224
224
  There are three paths.
225
225
 
@@ -277,7 +277,7 @@ export const aiToolOverrides: AiToolOverridesMap = {
277
277
  }
278
278
  ```
279
279
 
280
- **Path B — `modules.ts` inline (app-level static, unified `entry.overrides`).** Declare overrides under the umbrella `overrides.ai` key on a `ModuleEntry` inside `apps/<app>/src/modules.ts`. Other contracts a module presents (routes, events, workers, widgets, notifications, interceptors, setup, ACL, DI, encryption, …) reuse the same `entry.overrides` shape per spec `.ai/specs/2026-05-04-modules-ts-unified-overrides.md`; phases 1-18 are wired. The app's `bootstrap.ts` calls `applyModuleOverridesFromEnabledModules(enabledModules)` from `@open-mercato/shared/modules/overrides` once at boot — both `apps/mercato` and the `create-mercato-app` template ship that wiring.
280
+ **Path B — `modules.ts` inline (app-level static, unified `entry.overrides`).** Declare overrides under the umbrella `overrides.ai` key on a `ModuleEntry` inside `apps/<app>/src/modules.ts`. Other contracts a module presents (routes, events, workers, widgets, notifications, interceptors, setup, ACL, DI, encryption, …) reuse the same `entry.overrides` shape per spec `.ai/specs/implemented/2026-05-04-modules-ts-unified-overrides.md`; phases 1-18 are wired. The app's `bootstrap.ts` calls `applyModuleOverridesFromEnabledModules(enabledModules)` from `@open-mercato/shared/modules/overrides` once at boot — both `apps/mercato` and the `create-mercato-app` template ship that wiring.
281
281
 
282
282
  ```ts
283
283
  // apps/<app>/src/modules.ts
@@ -1464,6 +1464,18 @@ Agents that need multi-step tool loops configure the `loop` block on `AiAgentDef
1464
1464
 
1465
1465
  ## Changelog
1466
1466
 
1467
+ ### 2026-06-11 - MCP stdio server fails closed without auth (#2673)
1468
+
1469
+ **What changed**:
1470
+ - `createMcpServer` / `runMcpServer` (`lib/mcp-server.ts`) no longer silently grant `isSuperAdmin = true` when no auth is supplied. The two fail-open branches (a `context` without a `userId`, and neither `apiKeySecret` nor `context`) now **throw** instead of escalating to an unscoped superadmin.
1471
+ - `apiKeySecret` is normalized — an empty / whitespace-only string is treated as missing instead of falling through to the unauthenticated branch.
1472
+ - Added an explicit, loud opt-in `McpServerOptions.allowUnauthenticatedSuperadmin` (default off) for local dev/testing. When enabled the server runs as superadmin with a startup `WARNING` log; when off and unauthenticated it refuses to start.
1473
+ - `mcp:serve` CLI: `--user` is now effectively required alongside `--tenant`; the legacy "no user → superadmin" behavior is preserved only behind the new `--allow-unauthenticated-superadmin` flag (documented in `--help`).
1474
+
1475
+ **Files modified**: `lib/mcp-server.ts`, `lib/types.ts`, `cli.ts`. Regression test: `lib/__tests__/mcp-server-auth-fail-closed.test.ts`.
1476
+
1477
+ **Backward compatibility**: `allowUnauthenticatedSuperadmin` is an additive optional field. The only behavior change is that a previously fail-open misconfiguration now fails closed — callers that relied on auth-less superadmin must pass the explicit opt-in.
1478
+
1467
1479
  ### 2026-05-13 - Remove dead `indexApiEndpoints` from MCP boot (#1876)
1468
1480
 
1469
1481
  **What changed**:
package/README.md CHANGED
@@ -140,7 +140,7 @@ curl http://localhost:4096/mcp
140
140
  ### Built-in Providers
141
141
 
142
142
  The registry ships 10 built-in providers via the ports & adapters
143
- architecture (see [`.ai/specs/2026-04-14-llm-provider-ports-and-adapters.md`](../../.ai/specs/2026-04-14-llm-provider-ports-and-adapters.md)).
143
+ architecture (see [`.ai/specs/implemented/2026-04-14-llm-provider-ports-and-adapters.md`](../../.ai/specs/implemented/2026-04-14-llm-provider-ports-and-adapters.md)).
144
144
 
145
145
  Native protocol adapters:
146
146
 
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { listAgents, getAgent } from "../lib/agent-registry.js";
2
+ import { listAgents, getAgent, loadAgentRegistry } from "../lib/agent-registry.js";
3
3
  import { hasRequiredFeatures } from "../lib/auth.js";
4
4
  import { defineAiTool } from "../lib/ai-tool-definition.js";
5
5
  import {
@@ -73,6 +73,7 @@ const listAgentsTool = defineAiTool({
73
73
  const input = listAgentsInput.parse(rawInput);
74
74
  let all = [];
75
75
  try {
76
+ await loadAgentRegistry();
76
77
  all = listAgents();
77
78
  } catch {
78
79
  all = [];
@@ -102,6 +103,7 @@ const describeAgentTool = defineAiTool({
102
103
  const input = describeAgentInput.parse(rawInput);
103
104
  let agent;
104
105
  try {
106
+ await loadAgentRegistry();
105
107
  agent = getAgent(input.agentId);
106
108
  } catch {
107
109
  agent = void 0;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/ai_assistant/ai-tools/meta-pack.ts"],
4
- "sourcesContent": ["/**\n * General-purpose `meta.*` tool pack (Phase 1 WS-C, Step 3.8).\n *\n * `list_agents` enumerates agents the caller can invoke; `describe_agent`\n * returns a serializable description (Zod output schema \u2192 JSON-Schema when\n * possible, otherwise a `non-serializable` marker). Both tools treat a\n * missing agent registry as an empty list \u2014 the chat runtime must not\n * crash if `ai-agents.generated.ts` has not been emitted yet.\n */\nimport { z } from 'zod'\nimport type { AiAgentDefinition } from '../lib/ai-agent-definition'\nimport { listAgents, getAgent } from '../lib/agent-registry'\nimport { hasRequiredFeatures } from '../lib/auth'\nimport { defineAiTool } from '../lib/ai-tool-definition'\nimport {\n TASK_PLAN_DETAIL_MAX_CHARS,\n TASK_PLAN_ID_MAX_CHARS,\n TASK_PLAN_LABEL_MAX_CHARS,\n TASK_PLAN_MAX_TASKS,\n TASK_PLAN_TOOL_NAME_MAX_CHARS,\n looksLikeHiddenReasoning,\n sanitizeAgentTaskPlanInput,\n} from '../lib/task-plan-labels'\n\nfunction summarizeAgent(agent: AiAgentDefinition): Record<string, unknown> {\n return {\n id: agent.id,\n moduleId: agent.moduleId,\n label: agent.label,\n description: agent.description,\n requiredFeatures: agent.requiredFeatures ?? [],\n allowedTools: agent.allowedTools,\n executionMode: agent.executionMode ?? 'chat',\n mutationPolicy: agent.mutationPolicy ?? 'read-only',\n readOnly: typeof agent.readOnly === 'boolean' ? agent.readOnly : true,\n maxSteps: agent.maxSteps ?? null,\n acceptedMediaTypes: agent.acceptedMediaTypes ?? [],\n domain: agent.domain ?? null,\n keywords: agent.keywords ?? [],\n suggestions: agent.suggestions ?? [],\n taskPlan: agent.taskPlan ?? { enabled: false },\n dataCapabilities: agent.dataCapabilities ?? null,\n hasOutputSchema: Boolean(agent.output),\n hasPageContextResolver: typeof agent.resolvePageContext === 'function',\n }\n}\n\nfunction serializeStructuredOutput(\n output: AiAgentDefinition['output'],\n): Record<string, unknown> | null {\n if (!output) return null\n try {\n const jsonSchema = z.toJSONSchema(output.schema as unknown as z.ZodType, {\n unrepresentable: 'any',\n })\n return {\n schemaName: output.schemaName,\n mode: output.mode ?? 'generate',\n jsonSchema,\n }\n } catch (error) {\n return {\n schemaName: output.schemaName,\n mode: output.mode ?? 'generate',\n note: 'non-serializable',\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\nfunction serializePrompt(agent: AiAgentDefinition): Record<string, unknown> {\n return {\n systemPrompt: agent.systemPrompt,\n hasDynamicPageContext: typeof agent.resolvePageContext === 'function',\n }\n}\n\nconst listAgentsInput = z.object({\n moduleId: z.string().optional().describe('Restrict results to one module id.'),\n})\n\nconst listAgentsTool = defineAiTool({\n name: 'meta.list_agents',\n displayName: 'List agents',\n description:\n 'List registered AI agents the caller can invoke. Filters by requiredFeatures RBAC; returns { agents: [] } if the registry is empty.',\n inputSchema: listAgentsInput,\n requiredFeatures: ['ai_assistant.view'],\n tags: ['read', 'meta'],\n handler: async (rawInput, ctx) => {\n const input = listAgentsInput.parse(rawInput)\n let all: AiAgentDefinition[] = []\n try {\n all = listAgents()\n } catch {\n all = []\n }\n const filtered = all.filter((agent) => {\n if (input.moduleId && agent.moduleId !== input.moduleId) return false\n const features = agent.requiredFeatures ?? []\n return hasRequiredFeatures(features, ctx.userFeatures, ctx.isSuperAdmin)\n })\n return {\n agents: filtered.map(summarizeAgent),\n total: filtered.length,\n }\n },\n})\n\nconst describeAgentInput = z.object({\n agentId: z.string().min(1).describe('Agent id (e.g. \"catalog.merchandising_assistant\").'),\n})\n\nconst describeAgentTool = defineAiTool({\n name: 'meta.describe_agent',\n displayName: 'Describe agent',\n description:\n 'Return metadata, RBAC, allowed tools, execution mode, output schema (JSON-Schema when representable), and prompt shape for a single agent. Never throws \u2014 returns { agent: null, reason } if missing or forbidden.',\n inputSchema: describeAgentInput,\n requiredFeatures: ['ai_assistant.view'],\n tags: ['read', 'meta'],\n handler: async (rawInput, ctx) => {\n const input = describeAgentInput.parse(rawInput)\n let agent: AiAgentDefinition | undefined\n try {\n agent = getAgent(input.agentId)\n } catch {\n agent = undefined\n }\n if (!agent) {\n return { agent: null, reason: 'not_found' as const }\n }\n const features = agent.requiredFeatures ?? []\n if (!hasRequiredFeatures(features, ctx.userFeatures, ctx.isSuperAdmin)) {\n return { agent: null, reason: 'forbidden' as const }\n }\n return {\n agent: {\n ...summarizeAgent(agent),\n output: serializeStructuredOutput(agent.output),\n prompt: serializePrompt(agent),\n },\n }\n },\n})\n\nconst visibleTaskLabel = z\n .string()\n .min(1)\n .max(TASK_PLAN_LABEL_MAX_CHARS)\n .superRefine((value, ctx) => {\n if (!looksLikeHiddenReasoning(value)) return\n ctx.addIssue({\n code: 'custom',\n message: 'Task-plan labels are user-visible UI copy and must not include private reasoning.',\n })\n })\n\nconst visibleTaskDetail = z\n .string()\n .max(TASK_PLAN_DETAIL_MAX_CHARS)\n .optional()\n .superRefine((value, ctx) => {\n if (!value || !looksLikeHiddenReasoning(value)) return\n ctx.addIssue({\n code: 'custom',\n message: 'Task-plan details must not include private reasoning.',\n })\n })\n\nconst updateTaskPlanInput = z.object({\n tasks: z\n .array(\n z.object({\n id: z.string().min(1).max(TASK_PLAN_ID_MAX_CHARS).optional(),\n label: visibleTaskLabel.describe('Concise user-visible step label. Do not include private reasoning.'),\n detail: visibleTaskDetail.describe('Optional short visible detail, not private reasoning.'),\n toolName: z\n .string()\n .min(1)\n .max(TASK_PLAN_TOOL_NAME_MAX_CHARS)\n .optional()\n .describe('Optional whitelisted tool name that this planned step maps to.'),\n }),\n )\n .min(1)\n .max(TASK_PLAN_MAX_TASKS),\n})\n\nconst updateTaskPlanTool = defineAiTool({\n name: 'meta.update_task_plan',\n displayName: 'Update task plan',\n description:\n 'Set the concise user-visible task plan for this assistant turn before calling domain tools. Labels are progress UI, not hidden reasoning.',\n inputSchema: updateTaskPlanInput,\n requiredFeatures: ['ai_assistant.view'],\n tags: ['read', 'meta', 'task-plan'],\n isMutation: false,\n handler: async (rawInput) => {\n const input = updateTaskPlanInput.parse(rawInput)\n const sanitized = sanitizeAgentTaskPlanInput(input)\n return {\n ok: sanitized.tasks.length > 0,\n tasks: sanitized.tasks,\n accepted: sanitized.tasks.length,\n dropped: input.tasks.length - sanitized.tasks.length,\n }\n },\n})\n\nexport const metaAiTools = [\n listAgentsTool,\n describeAgentTool,\n updateTaskPlanTool,\n]\n\nexport default metaAiTools\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * General-purpose `meta.*` tool pack (Phase 1 WS-C, Step 3.8).\n *\n * `list_agents` enumerates agents the caller can invoke; `describe_agent`\n * returns a serializable description (Zod output schema \u2192 JSON-Schema when\n * possible, otherwise a `non-serializable` marker). Both tools treat a\n * missing agent registry as an empty list \u2014 the chat runtime must not\n * crash if `ai-agents.generated.ts` has not been emitted yet.\n */\nimport { z } from 'zod'\nimport type { AiAgentDefinition } from '../lib/ai-agent-definition'\nimport { listAgents, getAgent, loadAgentRegistry } from '../lib/agent-registry'\nimport { hasRequiredFeatures } from '../lib/auth'\nimport { defineAiTool } from '../lib/ai-tool-definition'\nimport {\n TASK_PLAN_DETAIL_MAX_CHARS,\n TASK_PLAN_ID_MAX_CHARS,\n TASK_PLAN_LABEL_MAX_CHARS,\n TASK_PLAN_MAX_TASKS,\n TASK_PLAN_TOOL_NAME_MAX_CHARS,\n looksLikeHiddenReasoning,\n sanitizeAgentTaskPlanInput,\n} from '../lib/task-plan-labels'\n\nfunction summarizeAgent(agent: AiAgentDefinition): Record<string, unknown> {\n return {\n id: agent.id,\n moduleId: agent.moduleId,\n label: agent.label,\n description: agent.description,\n requiredFeatures: agent.requiredFeatures ?? [],\n allowedTools: agent.allowedTools,\n executionMode: agent.executionMode ?? 'chat',\n mutationPolicy: agent.mutationPolicy ?? 'read-only',\n readOnly: typeof agent.readOnly === 'boolean' ? agent.readOnly : true,\n maxSteps: agent.maxSteps ?? null,\n acceptedMediaTypes: agent.acceptedMediaTypes ?? [],\n domain: agent.domain ?? null,\n keywords: agent.keywords ?? [],\n suggestions: agent.suggestions ?? [],\n taskPlan: agent.taskPlan ?? { enabled: false },\n dataCapabilities: agent.dataCapabilities ?? null,\n hasOutputSchema: Boolean(agent.output),\n hasPageContextResolver: typeof agent.resolvePageContext === 'function',\n }\n}\n\nfunction serializeStructuredOutput(\n output: AiAgentDefinition['output'],\n): Record<string, unknown> | null {\n if (!output) return null\n try {\n const jsonSchema = z.toJSONSchema(output.schema as unknown as z.ZodType, {\n unrepresentable: 'any',\n })\n return {\n schemaName: output.schemaName,\n mode: output.mode ?? 'generate',\n jsonSchema,\n }\n } catch (error) {\n return {\n schemaName: output.schemaName,\n mode: output.mode ?? 'generate',\n note: 'non-serializable',\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\nfunction serializePrompt(agent: AiAgentDefinition): Record<string, unknown> {\n return {\n systemPrompt: agent.systemPrompt,\n hasDynamicPageContext: typeof agent.resolvePageContext === 'function',\n }\n}\n\nconst listAgentsInput = z.object({\n moduleId: z.string().optional().describe('Restrict results to one module id.'),\n})\n\nconst listAgentsTool = defineAiTool({\n name: 'meta.list_agents',\n displayName: 'List agents',\n description:\n 'List registered AI agents the caller can invoke. Filters by requiredFeatures RBAC; returns { agents: [] } if the registry is empty.',\n inputSchema: listAgentsInput,\n requiredFeatures: ['ai_assistant.view'],\n tags: ['read', 'meta'],\n handler: async (rawInput, ctx) => {\n const input = listAgentsInput.parse(rawInput)\n let all: AiAgentDefinition[] = []\n try {\n // Lazy-load the registry on first use. The in-app agents route loads it\n // at request time; standalone MCP servers have no such bootstrap, so the\n // tool must ensure it itself. Idempotent \u2014 subsequent calls are no-ops.\n await loadAgentRegistry()\n all = listAgents()\n } catch {\n all = []\n }\n const filtered = all.filter((agent) => {\n if (input.moduleId && agent.moduleId !== input.moduleId) return false\n const features = agent.requiredFeatures ?? []\n return hasRequiredFeatures(features, ctx.userFeatures, ctx.isSuperAdmin)\n })\n return {\n agents: filtered.map(summarizeAgent),\n total: filtered.length,\n }\n },\n})\n\nconst describeAgentInput = z.object({\n agentId: z.string().min(1).describe('Agent id (e.g. \"catalog.merchandising_assistant\").'),\n})\n\nconst describeAgentTool = defineAiTool({\n name: 'meta.describe_agent',\n displayName: 'Describe agent',\n description:\n 'Return metadata, RBAC, allowed tools, execution mode, output schema (JSON-Schema when representable), and prompt shape for a single agent. Never throws \u2014 returns { agent: null, reason } if missing or forbidden.',\n inputSchema: describeAgentInput,\n requiredFeatures: ['ai_assistant.view'],\n tags: ['read', 'meta'],\n handler: async (rawInput, ctx) => {\n const input = describeAgentInput.parse(rawInput)\n let agent: AiAgentDefinition | undefined\n try {\n // Lazy-load the registry on first use (see meta.list_agents). Idempotent.\n await loadAgentRegistry()\n agent = getAgent(input.agentId)\n } catch {\n agent = undefined\n }\n if (!agent) {\n return { agent: null, reason: 'not_found' as const }\n }\n const features = agent.requiredFeatures ?? []\n if (!hasRequiredFeatures(features, ctx.userFeatures, ctx.isSuperAdmin)) {\n return { agent: null, reason: 'forbidden' as const }\n }\n return {\n agent: {\n ...summarizeAgent(agent),\n output: serializeStructuredOutput(agent.output),\n prompt: serializePrompt(agent),\n },\n }\n },\n})\n\nconst visibleTaskLabel = z\n .string()\n .min(1)\n .max(TASK_PLAN_LABEL_MAX_CHARS)\n .superRefine((value, ctx) => {\n if (!looksLikeHiddenReasoning(value)) return\n ctx.addIssue({\n code: 'custom',\n message: 'Task-plan labels are user-visible UI copy and must not include private reasoning.',\n })\n })\n\nconst visibleTaskDetail = z\n .string()\n .max(TASK_PLAN_DETAIL_MAX_CHARS)\n .optional()\n .superRefine((value, ctx) => {\n if (!value || !looksLikeHiddenReasoning(value)) return\n ctx.addIssue({\n code: 'custom',\n message: 'Task-plan details must not include private reasoning.',\n })\n })\n\nconst updateTaskPlanInput = z.object({\n tasks: z\n .array(\n z.object({\n id: z.string().min(1).max(TASK_PLAN_ID_MAX_CHARS).optional(),\n label: visibleTaskLabel.describe('Concise user-visible step label. Do not include private reasoning.'),\n detail: visibleTaskDetail.describe('Optional short visible detail, not private reasoning.'),\n toolName: z\n .string()\n .min(1)\n .max(TASK_PLAN_TOOL_NAME_MAX_CHARS)\n .optional()\n .describe('Optional whitelisted tool name that this planned step maps to.'),\n }),\n )\n .min(1)\n .max(TASK_PLAN_MAX_TASKS),\n})\n\nconst updateTaskPlanTool = defineAiTool({\n name: 'meta.update_task_plan',\n displayName: 'Update task plan',\n description:\n 'Set the concise user-visible task plan for this assistant turn before calling domain tools. Labels are progress UI, not hidden reasoning.',\n inputSchema: updateTaskPlanInput,\n requiredFeatures: ['ai_assistant.view'],\n tags: ['read', 'meta', 'task-plan'],\n isMutation: false,\n handler: async (rawInput) => {\n const input = updateTaskPlanInput.parse(rawInput)\n const sanitized = sanitizeAgentTaskPlanInput(input)\n return {\n ok: sanitized.tasks.length > 0,\n tasks: sanitized.tasks,\n accepted: sanitized.tasks.length,\n dropped: input.tasks.length - sanitized.tasks.length,\n }\n },\n})\n\nexport const metaAiTools = [\n listAgentsTool,\n describeAgentTool,\n updateTaskPlanTool,\n]\n\nexport default metaAiTools\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,95 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
4
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
5
+ import { createApiKey } from "@open-mercato/core/modules/api_keys/services/apiKeyService";
6
+ import { UserRole } from "@open-mercato/core/modules/auth/data/entities";
7
+ import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
8
+ const metadata = {
9
+ POST: { requireAuth: true, requireFeatures: ["api_keys.create"] }
10
+ };
11
+ async function getUserRoleIds(em, userId, tenantId) {
12
+ if (!tenantId) return [];
13
+ const links = await findWithDecryption(
14
+ em,
15
+ UserRole,
16
+ { user: userId, role: { tenantId } },
17
+ { populate: ["role"] },
18
+ { tenantId, organizationId: null }
19
+ );
20
+ const linkList = Array.isArray(links) ? links : [];
21
+ return linkList.map((link) => link.role?.id).filter((id) => typeof id === "string" && id.length > 0);
22
+ }
23
+ const bodySchema = z.object({
24
+ name: z.string().min(1).max(120).optional(),
25
+ description: z.string().max(1e3).optional().nullable()
26
+ });
27
+ async function POST(req) {
28
+ const auth = await getAuthFromRequest(req);
29
+ if (!auth?.sub) {
30
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
31
+ }
32
+ try {
33
+ const raw = await req.json().catch(() => ({}));
34
+ const body = bodySchema.parse(raw ?? {});
35
+ const container = await createRequestContainer();
36
+ const em = container.resolve("em");
37
+ const roleIds = await getUserRoleIds(em, auth.sub, auth.tenantId);
38
+ const { record, secret } = await createApiKey(em, {
39
+ name: body.name ?? `MCP Config - ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`,
40
+ description: body.description ?? "Generated from AI Assistant settings for MCP client",
41
+ tenantId: auth.tenantId ?? null,
42
+ organizationId: auth.orgId ?? null,
43
+ roles: roleIds,
44
+ createdBy: auth.sub
45
+ });
46
+ return NextResponse.json({
47
+ id: record.id,
48
+ name: record.name,
49
+ keyPrefix: record.keyPrefix,
50
+ secret,
51
+ tenantId: record.tenantId ?? null,
52
+ organizationId: record.organizationId ?? null,
53
+ roles: roleIds
54
+ });
55
+ } catch (error) {
56
+ console.error("[MCP Key] Error:", error);
57
+ return NextResponse.json(
58
+ { error: "Failed to create MCP API key" },
59
+ { status: 500 }
60
+ );
61
+ }
62
+ }
63
+ const responseSchema = z.object({
64
+ id: z.string(),
65
+ name: z.string(),
66
+ keyPrefix: z.string(),
67
+ secret: z.string().describe("Full API key value (omk_...). Shown once."),
68
+ tenantId: z.string().uuid().nullable(),
69
+ organizationId: z.string().uuid().nullable(),
70
+ roles: z.array(z.string()).describe("Role ids inherited from the calling user.")
71
+ });
72
+ const errorSchema = z.object({ error: z.string() });
73
+ const openApi = {
74
+ summary: "Create MCP API key",
75
+ description: "Creates a persistent MCP API key that inherits the calling user's roles, for use in an MCP client `.mcp.json`.",
76
+ methods: {
77
+ POST: {
78
+ summary: "Generate MCP API key",
79
+ description: "Generates a persistent `omk_` API key scoped to the calling user's tenant/organization and carrying the caller's own roles, so the key has the same ACL as the user.",
80
+ responses: [
81
+ { status: 200, description: "API key created", schema: responseSchema }
82
+ ],
83
+ errors: [
84
+ { status: 401, description: "Unauthorized", schema: errorSchema },
85
+ { status: 500, description: "Failed to create MCP API key", schema: errorSchema }
86
+ ]
87
+ }
88
+ }
89
+ };
90
+ export {
91
+ POST,
92
+ metadata,
93
+ openApi
94
+ };
95
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/ai_assistant/api/mcp-key/route.ts"],
4
+ "sourcesContent": ["import { NextResponse, type NextRequest } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { createApiKey } from '@open-mercato/core/modules/api_keys/services/apiKeyService'\nimport { UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['api_keys.create'] },\n}\n\n/**\n * Resolve the calling user's role ids for the active tenant. Mirrors the\n * `session-key` route so the generated key carries exactly the caller's roles.\n */\nasync function getUserRoleIds(\n em: EntityManager,\n userId: string,\n tenantId: string | null,\n): Promise<string[]> {\n if (!tenantId) return []\n const links = await findWithDecryption(\n em,\n UserRole,\n { user: userId as any, role: { tenantId } } as any,\n { populate: ['role'] },\n { tenantId, organizationId: null },\n )\n const linkList = Array.isArray(links) ? links : []\n return linkList\n .map((link) => (link.role as any)?.id)\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n}\n\nconst bodySchema = z.object({\n name: z.string().min(1).max(120).optional(),\n description: z.string().max(1000).optional().nullable(),\n})\n\n/**\n * POST /api/ai_assistant/mcp-key\n *\n * Creates a persistent MCP API key (`omk_...`) for use in `.mcp.json`. Unlike a\n * raw `POST /api/api_keys/keys` call \u2014 which assigns no roles unless the client\n * passes them \u2014 this endpoint resolves the **caller's own roles** server-side\n * and attaches them to the key, so the key inherits the current user's ACL.\n * The user cannot escalate: they only ever grant their own roles to their own\n * key.\n */\nexport async function POST(req: NextRequest) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n try {\n const raw = await req.json().catch(() => ({}))\n const body = bodySchema.parse(raw ?? {})\n\n const container = await createRequestContainer()\n const em = container.resolve<EntityManager>('em')\n\n const roleIds = await getUserRoleIds(em, auth.sub, auth.tenantId)\n\n const { record, secret } = await createApiKey(em, {\n name: body.name ?? `MCP Config - ${new Date().toISOString().slice(0, 10)}`,\n description: body.description ?? 'Generated from AI Assistant settings for MCP client',\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n roles: roleIds,\n createdBy: auth.sub,\n })\n\n return NextResponse.json({\n id: record.id,\n name: record.name,\n keyPrefix: record.keyPrefix,\n secret,\n tenantId: record.tenantId ?? null,\n organizationId: record.organizationId ?? null,\n roles: roleIds,\n })\n } catch (error) {\n console.error('[MCP Key] Error:', error)\n return NextResponse.json(\n { error: 'Failed to create MCP API key' },\n { status: 500 },\n )\n }\n}\n\nconst responseSchema = z.object({\n id: z.string(),\n name: z.string(),\n keyPrefix: z.string(),\n secret: z.string().describe('Full API key value (omk_...). Shown once.'),\n tenantId: z.string().uuid().nullable(),\n organizationId: z.string().uuid().nullable(),\n roles: z.array(z.string()).describe('Role ids inherited from the calling user.'),\n})\n\nconst errorSchema = z.object({ error: z.string() })\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Create MCP API key',\n description:\n 'Creates a persistent MCP API key that inherits the calling user\\'s roles, for use in an MCP client `.mcp.json`.',\n methods: {\n POST: {\n summary: 'Generate MCP API key',\n description:\n 'Generates a persistent `omk_` API key scoped to the calling user\\'s tenant/organization and carrying the caller\\'s own roles, so the key has the same ACL as the user.',\n responses: [\n { status: 200, description: 'API key created', schema: responseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 500, description: 'Failed to create MCP API key', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -34,28 +34,35 @@ const mcpServe = {
34
34
  command: "mcp:serve",
35
35
  async run(rest) {
36
36
  const args = parseArgs(rest);
37
- const apiKey = String(args["api-key"] ?? args.apiKey ?? "") || null;
37
+ const apiKey = String(args["api-key"] ?? args.apiKey ?? "") || process.env.OPEN_MERCATO_API_KEY || null;
38
38
  const tenantId = String(args.tenant ?? args.tenantId ?? "") || null;
39
39
  const organizationId = String(args.org ?? args.organizationId ?? "") || null;
40
40
  const userId = String(args.user ?? args.userId ?? "") || null;
41
41
  const debug = args.debug === true || args.debug === "true";
42
+ const allowUnauthenticatedSuperadmin = args["allow-unauthenticated-superadmin"] === true || args["allow-unauthenticated-superadmin"] === "true";
42
43
  if (!apiKey && !tenantId) {
43
44
  console.error("Usage: mercato ai_assistant mcp:serve [options]");
44
45
  console.error("");
45
46
  console.error("Authentication (choose one):");
46
- console.error(" --api-key <secret> API key secret for authentication (recommended)");
47
- console.error(" --tenant <id> Tenant ID (for manual context)");
47
+ console.error(" OPEN_MERCATO_API_KEY env API key secret (recommended \u2014 keeps the secret off argv)");
48
+ console.error(" --api-key <secret> API key secret for authentication (visible in process listings)");
49
+ console.error(" --tenant <id> Tenant ID (for manual context)");
48
50
  console.error("");
49
51
  console.error("Options (with --tenant):");
50
52
  console.error(" --org <id> Organization ID (optional)");
51
- console.error(" --user <id> User ID for ACL (optional, uses superadmin if not set)");
53
+ console.error(" --user <id> User ID for ACL (required unless --allow-unauthenticated-superadmin is set)");
52
54
  console.error("");
53
55
  console.error("Common options:");
54
56
  console.error(" --debug Enable debug logging");
57
+ console.error(" --allow-unauthenticated-superadmin");
58
+ console.error(" DEV/TEST ONLY: run as superadmin with no per-user ACL when");
59
+ console.error(" no --user (and no --api-key) is supplied. Never use in production.");
55
60
  console.error("");
56
61
  console.error("Examples:");
62
+ console.error(" OPEN_MERCATO_API_KEY=omk_xxxx.yyyy... mercato ai_assistant mcp:serve");
57
63
  console.error(" mercato ai_assistant mcp:serve --api-key omk_xxxx.yyyy...");
58
- console.error(" mercato ai_assistant mcp:serve --tenant 123e4567-e89b-12d3-a456-426614174000");
64
+ console.error(" mercato ai_assistant mcp:serve --tenant 123e4567-e89b-12d3-a456-426614174000 --user <user-id>");
65
+ console.error(" mercato ai_assistant mcp:serve --tenant 123e4567-e89b-12d3-a456-426614174000 --allow-unauthenticated-superadmin");
59
66
  return;
60
67
  }
61
68
  await ensureBootstrap();
@@ -83,7 +90,8 @@ const mcpServe = {
83
90
  tenantId,
84
91
  organizationId,
85
92
  userId
86
- }
93
+ },
94
+ allowUnauthenticatedSuperadmin
87
95
  });
88
96
  }
89
97
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/ai_assistant/cli.ts"],
4
- "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\n/**\n * Ensure app bootstrap is called before creating DI container.\n * Uses the shared generated-bootstrap loader so the command works both from\n * the monorepo app and from standalone apps installed through npm packages.\n */\nasync function ensureBootstrap(): Promise<void> {\n // First check if DI is already available\n try {\n const { getDiRegistrars } = await import('@open-mercato/shared/lib/di/container')\n getDiRegistrars()\n return // DI already available\n } catch {\n // DI not available, need to bootstrap\n }\n\n try {\n const { bootstrapFromAppRoot } = await import('@open-mercato/shared/lib/bootstrap/dynamicLoader')\n await bootstrapFromAppRoot()\n } catch (error) {\n console.error('[MCP] Bootstrap failed:', error instanceof Error ? error.message : error)\n // Continue - some contexts may not have bootstrap available\n }\n}\n\nfunction parseArgs(rest: string[]): Record<string, string | boolean> {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i]\n if (!arg?.startsWith('--')) continue\n\n const [key, value] = arg.replace(/^--/, '').split('=')\n if (value !== undefined) {\n args[key] = value\n } else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) {\n args[key] = rest[i + 1]!\n i++\n } else {\n args[key] = true\n }\n }\n return args\n}\n\nconst mcpServe: ModuleCli = {\n command: 'mcp:serve',\n async run(rest) {\n const args = parseArgs(rest)\n const apiKey = String(args['api-key'] ?? args.apiKey ?? '') || null\n const tenantId = String(args.tenant ?? args.tenantId ?? '') || null\n const organizationId = String(args.org ?? args.organizationId ?? '') || null\n const userId = String(args.user ?? args.userId ?? '') || null\n const debug = args.debug === true || args.debug === 'true'\n\n // Either API key or tenant is required\n if (!apiKey && !tenantId) {\n console.error('Usage: mercato ai_assistant mcp:serve [options]')\n console.error('')\n console.error('Authentication (choose one):')\n console.error(' --api-key <secret> API key secret for authentication (recommended)')\n console.error(' --tenant <id> Tenant ID (for manual context)')\n console.error('')\n console.error('Options (with --tenant):')\n console.error(' --org <id> Organization ID (optional)')\n console.error(' --user <id> User ID for ACL (optional, uses superadmin if not set)')\n console.error('')\n console.error('Common options:')\n console.error(' --debug Enable debug logging')\n console.error('')\n console.error('Examples:')\n console.error(' mercato ai_assistant mcp:serve --api-key omk_xxxx.yyyy...')\n console.error(' mercato ai_assistant mcp:serve --tenant 123e4567-e89b-12d3-a456-426614174000')\n return\n }\n\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runMcpServer } = await import('./lib/mcp-server')\n\n if (apiKey) {\n await runMcpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n apiKeySecret: apiKey,\n })\n } else {\n await runMcpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n context: {\n tenantId,\n organizationId,\n userId,\n },\n })\n }\n },\n}\n\nconst MCP_DEFAULT_PORT = 3001\n\nconst mcpServeHttp: ModuleCli = {\n command: 'mcp:serve-http',\n async run(rest) {\n const args = parseArgs(rest)\n const portArg = parseInt(String(args.port ?? ''), 10)\n const port = !portArg || isNaN(portArg) ? MCP_DEFAULT_PORT : portArg\n const debug = args.debug === true || args.debug === 'true'\n\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runMcpHttpServer } = await import('./lib/http-server')\n\n await runMcpHttpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n port,\n })\n },\n}\n\nconst mcpDev: ModuleCli = {\n command: 'mcp:dev',\n async run() {\n await ensureBootstrap()\n const { runMcpDevServer } = await import('./lib/mcp-dev-server')\n await runMcpDevServer()\n },\n}\n\nconst listTools: ModuleCli = {\n command: 'mcp:list-tools',\n async run(rest) {\n const args = parseArgs(rest)\n const verbose = args.verbose === true || args.verbose === 'true'\n\n // Ensure bootstrap runs so modules are registered for API discovery\n await ensureBootstrap()\n\n const { loadAllModuleTools } = await import('./lib/tool-loader')\n await loadAllModuleTools()\n\n const { getToolRegistry } = await import('./lib/tool-registry')\n const registry = getToolRegistry()\n const toolNames = registry.listToolNames()\n\n if (toolNames.length === 0) {\n console.log('\\nNo MCP tools registered.')\n console.log('Tools can be registered by modules using registerMcpTool().\\n')\n return\n }\n\n console.log(`\\nRegistered MCP Tools (${toolNames.length}):\\n`)\n\n // Group tools by module\n const byModule = new Map<string, string[]>()\n for (const name of toolNames) {\n const [module] = name.split('.')\n const list = byModule.get(module) ?? []\n list.push(name)\n byModule.set(module, list)\n }\n\n // Sort modules alphabetically\n const sortedModules = Array.from(byModule.keys()).sort()\n\n for (const module of sortedModules) {\n const tools = byModule.get(module)!\n console.log(`${module} (${tools.length} tools):`)\n\n for (const name of tools.sort()) {\n const tool = registry.getTool(name)\n if (!tool) continue\n\n if (verbose) {\n console.log(` ${name}`)\n console.log(` ${tool.description}`)\n if (tool.requiredFeatures?.length) {\n console.log(` Requires: ${tool.requiredFeatures.join(', ')}`)\n }\n } else {\n console.log(` - ${name}`)\n }\n }\n console.log('')\n }\n },\n}\n\nconst entityGraph: ModuleCli = {\n command: 'entity-graph',\n async run(rest) {\n const args = parseArgs(rest)\n const format = String(args.format ?? 'triples') as 'json' | 'triples'\n const entity = args.entity ? String(args.entity) : undefined\n const module = args.module ? String(args.module) : undefined\n\n await ensureBootstrap()\n\n const { getOrm } = await import('@open-mercato/shared/lib/db/mikro')\n const { extractEntityGraph, formatGraphAsTriples, filterGraphByEntity, filterGraphByModule } = await import(\n './lib/entity-graph'\n )\n\n console.log('[Entity Graph] Extracting from MikroORM metadata...')\n\n const orm = await getOrm()\n const graph = await extractEntityGraph(orm)\n\n // Apply filters\n let edges = graph.edges\n\n if (entity) {\n edges = filterGraphByEntity(graph, entity)\n console.log(`[Entity Graph] Filtered by entity: ${entity}`)\n }\n\n if (module) {\n const filteredGraph = { ...graph, edges }\n edges = filterGraphByModule(filteredGraph, module)\n console.log(`[Entity Graph] Filtered by module: ${module}`)\n }\n\n const filteredGraph = { ...graph, edges }\n\n if (format === 'json') {\n console.log(JSON.stringify(filteredGraph, null, 2))\n } else {\n const triples = formatGraphAsTriples(filteredGraph)\n console.log('')\n for (const triple of triples) {\n console.log(triple)\n }\n }\n\n console.log(`\\n[Entity Graph] ${graph.nodes.length} entities, ${edges.length} relationships`)\n },\n}\n\nconst runPendingActionCleanup: ModuleCli = {\n command: 'run-pending-action-cleanup',\n async run() {\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runPendingActionCleanup: runCleanup } = await import(\n './workers/ai-pending-action-cleanup'\n )\n\n const em = container.resolve<import('@mikro-orm/postgresql').EntityManager>('em')\n const summary = await runCleanup({ em })\n\n console.log('[ai-pending-action-cleanup] Sweep complete:', summary)\n },\n}\n\nconst testTools: ModuleCli = {\n command: 'test-tools',\n async run(rest) {\n const args = parseArgs(rest)\n const json = args.json === true || args.json === 'true'\n const moduleFilter =\n typeof args.module === 'string' && args.module.length > 0 ? args.module : null\n const includeMutations = args['no-mutations'] !== true && args['no-mutations'] !== 'true'\n const tenantId =\n typeof args.tenant === 'string' && args.tenant.length > 0 ? args.tenant : null\n const organizationId =\n typeof args.org === 'string' && args.org.length > 0 ? args.org : null\n\n await ensureBootstrap()\n const { runToolTests } = await import('./lib/tool-test-runner')\n const report = await runToolTests({\n tenantId,\n organizationId,\n moduleFilter,\n includeMutations,\n })\n\n if (json) {\n // Wrap in markers so a Playwright spec (or any caller) can extract the\n // JSON payload without being thrown off by bootstrap log lines emitted\n // to stdout by other modules during DI container creation.\n console.log('---TOOL_TEST_REPORT_BEGIN---')\n console.log(JSON.stringify(report))\n console.log('---TOOL_TEST_REPORT_END---')\n } else {\n console.log('')\n console.log(\n `AI tool test report \u2014 tenant=${report.tenantId ?? '<none>'} org=${\n report.organizationId ?? '<none>'\n }`,\n )\n console.log(\n `total=${report.total} pass=${report.passed} fail=${report.failed} skip=${report.skipped}`,\n )\n const byModule = new Map<string, typeof report.records>()\n for (const record of report.records) {\n const list = byModule.get(record.module) ?? []\n list.push(record)\n byModule.set(record.module, list)\n }\n const sortedModules = Array.from(byModule.keys()).sort()\n for (const moduleId of sortedModules) {\n const list = byModule.get(moduleId)!\n console.log('')\n console.log(`${moduleId} (${list.length}):`)\n for (const record of list) {\n const marker =\n record.status === 'pass' ? '\u2713' : record.status === 'fail' ? '\u2717' : '\u00B7'\n const reason = record.reason ? ` \u2014 ${record.reason}` : ''\n const mutation = record.isMutation ? ' [mutation]' : ''\n console.log(\n ` ${marker} ${record.tool}${mutation} (${record.durationMs}ms)${reason}`,\n )\n }\n }\n console.log('')\n }\n\n if (report.failed > 0) {\n process.exitCode = 1\n }\n },\n}\n\nconst runTokenUsagePrune: ModuleCli = {\n command: 'run-token-usage-prune',\n async run() {\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runTokenUsagePrune: runPrune } = await import(\n './workers/ai-token-usage-prune'\n )\n\n const em = container.resolve<import('@mikro-orm/postgresql').EntityManager>('em')\n const summary = await runPrune({ em })\n\n console.log('[ai-token-usage-prune] Prune complete:', summary)\n },\n}\n\nexport default [\n mcpServe,\n mcpServeHttp,\n mcpDev,\n listTools,\n entityGraph,\n runPendingActionCleanup,\n runTokenUsagePrune,\n testTools,\n]\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\n/**\n * Ensure app bootstrap is called before creating DI container.\n * Uses the shared generated-bootstrap loader so the command works both from\n * the monorepo app and from standalone apps installed through npm packages.\n */\nasync function ensureBootstrap(): Promise<void> {\n // First check if DI is already available\n try {\n const { getDiRegistrars } = await import('@open-mercato/shared/lib/di/container')\n getDiRegistrars()\n return // DI already available\n } catch {\n // DI not available, need to bootstrap\n }\n\n try {\n const { bootstrapFromAppRoot } = await import('@open-mercato/shared/lib/bootstrap/dynamicLoader')\n await bootstrapFromAppRoot()\n } catch (error) {\n console.error('[MCP] Bootstrap failed:', error instanceof Error ? error.message : error)\n // Continue - some contexts may not have bootstrap available\n }\n}\n\nfunction parseArgs(rest: string[]): Record<string, string | boolean> {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i]\n if (!arg?.startsWith('--')) continue\n\n const [key, value] = arg.replace(/^--/, '').split('=')\n if (value !== undefined) {\n args[key] = value\n } else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) {\n args[key] = rest[i + 1]!\n i++\n } else {\n args[key] = true\n }\n }\n return args\n}\n\nconst mcpServe: ModuleCli = {\n command: 'mcp:serve',\n async run(rest) {\n const args = parseArgs(rest)\n // Prefer the OPEN_MERCATO_API_KEY env var so the secret never has to be\n // placed on the command line (argv is world-readable via ps / /proc).\n // The --api-key flag stays supported for backward compatibility.\n const apiKey = String(args['api-key'] ?? args.apiKey ?? '') || process.env.OPEN_MERCATO_API_KEY || null\n const tenantId = String(args.tenant ?? args.tenantId ?? '') || null\n const organizationId = String(args.org ?? args.organizationId ?? '') || null\n const userId = String(args.user ?? args.userId ?? '') || null\n const debug = args.debug === true || args.debug === 'true'\n const allowUnauthenticatedSuperadmin =\n args['allow-unauthenticated-superadmin'] === true ||\n args['allow-unauthenticated-superadmin'] === 'true'\n\n // Either API key or tenant is required\n if (!apiKey && !tenantId) {\n console.error('Usage: mercato ai_assistant mcp:serve [options]')\n console.error('')\n console.error('Authentication (choose one):')\n console.error(' OPEN_MERCATO_API_KEY env API key secret (recommended \u2014 keeps the secret off argv)')\n console.error(' --api-key <secret> API key secret for authentication (visible in process listings)')\n console.error(' --tenant <id> Tenant ID (for manual context)')\n console.error('')\n console.error('Options (with --tenant):')\n console.error(' --org <id> Organization ID (optional)')\n console.error(' --user <id> User ID for ACL (required unless --allow-unauthenticated-superadmin is set)')\n console.error('')\n console.error('Common options:')\n console.error(' --debug Enable debug logging')\n console.error(' --allow-unauthenticated-superadmin')\n console.error(' DEV/TEST ONLY: run as superadmin with no per-user ACL when')\n console.error(' no --user (and no --api-key) is supplied. Never use in production.')\n console.error('')\n console.error('Examples:')\n console.error(' OPEN_MERCATO_API_KEY=omk_xxxx.yyyy... mercato ai_assistant mcp:serve')\n console.error(' mercato ai_assistant mcp:serve --api-key omk_xxxx.yyyy...')\n console.error(' mercato ai_assistant mcp:serve --tenant 123e4567-e89b-12d3-a456-426614174000 --user <user-id>')\n console.error(' mercato ai_assistant mcp:serve --tenant 123e4567-e89b-12d3-a456-426614174000 --allow-unauthenticated-superadmin')\n return\n }\n\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runMcpServer } = await import('./lib/mcp-server')\n\n if (apiKey) {\n await runMcpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n apiKeySecret: apiKey,\n })\n } else {\n await runMcpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n context: {\n tenantId,\n organizationId,\n userId,\n },\n allowUnauthenticatedSuperadmin,\n })\n }\n },\n}\n\nconst MCP_DEFAULT_PORT = 3001\n\nconst mcpServeHttp: ModuleCli = {\n command: 'mcp:serve-http',\n async run(rest) {\n const args = parseArgs(rest)\n const portArg = parseInt(String(args.port ?? ''), 10)\n const port = !portArg || isNaN(portArg) ? MCP_DEFAULT_PORT : portArg\n const debug = args.debug === true || args.debug === 'true'\n\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runMcpHttpServer } = await import('./lib/http-server')\n\n await runMcpHttpServer({\n config: {\n name: 'open-mercato-mcp',\n version: '0.1.0',\n debug,\n },\n container,\n port,\n })\n },\n}\n\nconst mcpDev: ModuleCli = {\n command: 'mcp:dev',\n async run() {\n await ensureBootstrap()\n const { runMcpDevServer } = await import('./lib/mcp-dev-server')\n await runMcpDevServer()\n },\n}\n\nconst listTools: ModuleCli = {\n command: 'mcp:list-tools',\n async run(rest) {\n const args = parseArgs(rest)\n const verbose = args.verbose === true || args.verbose === 'true'\n\n // Ensure bootstrap runs so modules are registered for API discovery\n await ensureBootstrap()\n\n const { loadAllModuleTools } = await import('./lib/tool-loader')\n await loadAllModuleTools()\n\n const { getToolRegistry } = await import('./lib/tool-registry')\n const registry = getToolRegistry()\n const toolNames = registry.listToolNames()\n\n if (toolNames.length === 0) {\n console.log('\\nNo MCP tools registered.')\n console.log('Tools can be registered by modules using registerMcpTool().\\n')\n return\n }\n\n console.log(`\\nRegistered MCP Tools (${toolNames.length}):\\n`)\n\n // Group tools by module\n const byModule = new Map<string, string[]>()\n for (const name of toolNames) {\n const [module] = name.split('.')\n const list = byModule.get(module) ?? []\n list.push(name)\n byModule.set(module, list)\n }\n\n // Sort modules alphabetically\n const sortedModules = Array.from(byModule.keys()).sort()\n\n for (const module of sortedModules) {\n const tools = byModule.get(module)!\n console.log(`${module} (${tools.length} tools):`)\n\n for (const name of tools.sort()) {\n const tool = registry.getTool(name)\n if (!tool) continue\n\n if (verbose) {\n console.log(` ${name}`)\n console.log(` ${tool.description}`)\n if (tool.requiredFeatures?.length) {\n console.log(` Requires: ${tool.requiredFeatures.join(', ')}`)\n }\n } else {\n console.log(` - ${name}`)\n }\n }\n console.log('')\n }\n },\n}\n\nconst entityGraph: ModuleCli = {\n command: 'entity-graph',\n async run(rest) {\n const args = parseArgs(rest)\n const format = String(args.format ?? 'triples') as 'json' | 'triples'\n const entity = args.entity ? String(args.entity) : undefined\n const module = args.module ? String(args.module) : undefined\n\n await ensureBootstrap()\n\n const { getOrm } = await import('@open-mercato/shared/lib/db/mikro')\n const { extractEntityGraph, formatGraphAsTriples, filterGraphByEntity, filterGraphByModule } = await import(\n './lib/entity-graph'\n )\n\n console.log('[Entity Graph] Extracting from MikroORM metadata...')\n\n const orm = await getOrm()\n const graph = await extractEntityGraph(orm)\n\n // Apply filters\n let edges = graph.edges\n\n if (entity) {\n edges = filterGraphByEntity(graph, entity)\n console.log(`[Entity Graph] Filtered by entity: ${entity}`)\n }\n\n if (module) {\n const filteredGraph = { ...graph, edges }\n edges = filterGraphByModule(filteredGraph, module)\n console.log(`[Entity Graph] Filtered by module: ${module}`)\n }\n\n const filteredGraph = { ...graph, edges }\n\n if (format === 'json') {\n console.log(JSON.stringify(filteredGraph, null, 2))\n } else {\n const triples = formatGraphAsTriples(filteredGraph)\n console.log('')\n for (const triple of triples) {\n console.log(triple)\n }\n }\n\n console.log(`\\n[Entity Graph] ${graph.nodes.length} entities, ${edges.length} relationships`)\n },\n}\n\nconst runPendingActionCleanup: ModuleCli = {\n command: 'run-pending-action-cleanup',\n async run() {\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runPendingActionCleanup: runCleanup } = await import(\n './workers/ai-pending-action-cleanup'\n )\n\n const em = container.resolve<import('@mikro-orm/postgresql').EntityManager>('em')\n const summary = await runCleanup({ em })\n\n console.log('[ai-pending-action-cleanup] Sweep complete:', summary)\n },\n}\n\nconst testTools: ModuleCli = {\n command: 'test-tools',\n async run(rest) {\n const args = parseArgs(rest)\n const json = args.json === true || args.json === 'true'\n const moduleFilter =\n typeof args.module === 'string' && args.module.length > 0 ? args.module : null\n const includeMutations = args['no-mutations'] !== true && args['no-mutations'] !== 'true'\n const tenantId =\n typeof args.tenant === 'string' && args.tenant.length > 0 ? args.tenant : null\n const organizationId =\n typeof args.org === 'string' && args.org.length > 0 ? args.org : null\n\n await ensureBootstrap()\n const { runToolTests } = await import('./lib/tool-test-runner')\n const report = await runToolTests({\n tenantId,\n organizationId,\n moduleFilter,\n includeMutations,\n })\n\n if (json) {\n // Wrap in markers so a Playwright spec (or any caller) can extract the\n // JSON payload without being thrown off by bootstrap log lines emitted\n // to stdout by other modules during DI container creation.\n console.log('---TOOL_TEST_REPORT_BEGIN---')\n console.log(JSON.stringify(report))\n console.log('---TOOL_TEST_REPORT_END---')\n } else {\n console.log('')\n console.log(\n `AI tool test report \u2014 tenant=${report.tenantId ?? '<none>'} org=${\n report.organizationId ?? '<none>'\n }`,\n )\n console.log(\n `total=${report.total} pass=${report.passed} fail=${report.failed} skip=${report.skipped}`,\n )\n const byModule = new Map<string, typeof report.records>()\n for (const record of report.records) {\n const list = byModule.get(record.module) ?? []\n list.push(record)\n byModule.set(record.module, list)\n }\n const sortedModules = Array.from(byModule.keys()).sort()\n for (const moduleId of sortedModules) {\n const list = byModule.get(moduleId)!\n console.log('')\n console.log(`${moduleId} (${list.length}):`)\n for (const record of list) {\n const marker =\n record.status === 'pass' ? '\u2713' : record.status === 'fail' ? '\u2717' : '\u00B7'\n const reason = record.reason ? ` \u2014 ${record.reason}` : ''\n const mutation = record.isMutation ? ' [mutation]' : ''\n console.log(\n ` ${marker} ${record.tool}${mutation} (${record.durationMs}ms)${reason}`,\n )\n }\n }\n console.log('')\n }\n\n if (report.failed > 0) {\n process.exitCode = 1\n }\n },\n}\n\nconst runTokenUsagePrune: ModuleCli = {\n command: 'run-token-usage-prune',\n async run() {\n await ensureBootstrap()\n const container = await createRequestContainer()\n\n const { runTokenUsagePrune: runPrune } = await import(\n './workers/ai-token-usage-prune'\n )\n\n const em = container.resolve<import('@mikro-orm/postgresql').EntityManager>('em')\n const summary = await runPrune({ em })\n\n console.log('[ai-token-usage-prune] Prune complete:', summary)\n },\n}\n\nexport default [\n mcpServe,\n mcpServeHttp,\n mcpDev,\n listTools,\n entityGraph,\n runPendingActionCleanup,\n runTokenUsagePrune,\n testTools,\n]\n"],
5
+ "mappings": "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",
6
6
  "names": ["filteredGraph"]
7
7
  }
@@ -25,7 +25,7 @@ function McpConfigDialog({ open, onOpenChange, mcpUrl }) {
25
25
  setIsGenerating(true);
26
26
  setError(null);
27
27
  try {
28
- const res = await apiCall("/api/api_keys/keys", {
28
+ const res = await apiCall("/api/ai_assistant/mcp-key", {
29
29
  method: "POST",
30
30
  body: JSON.stringify({
31
31
  name: `MCP Config - ${(/* @__PURE__ */ new Date()).toLocaleDateString()}`,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/ai_assistant/components/McpConfigDialog.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useState } from 'react'\nimport { Copy, RefreshCw, AlertTriangle, Check, Settings } from 'lucide-react'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useDialogKeyHandler } from '@open-mercato/ui/hooks/useDialogKeyHandler'\n\ntype Props = {\n open: boolean\n onOpenChange: (next: boolean) => void\n mcpUrl: string\n}\n\ntype ApiKeyResponse = {\n id: string\n name: string\n keyPrefix: string\n secret?: string\n}\n\nexport default function McpConfigDialog({ open, onOpenChange, mcpUrl }: Props) {\n const t = useT()\n const [apiKey, setApiKey] = useState<string | null>(null)\n const [isGenerating, setIsGenerating] = useState(false)\n const [copiedConfig, setCopiedConfig] = useState(false)\n const [copiedKey, setCopiedKey] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const generateApiKey = async () => {\n setIsGenerating(true)\n setError(null)\n try {\n const res = await apiCall<ApiKeyResponse>('/api/api_keys/keys', {\n method: 'POST',\n body: JSON.stringify({\n name: `MCP Config - ${new Date().toLocaleDateString()}`,\n description: 'Generated from AI Assistant settings for MCP client',\n }),\n })\n if (res.ok && res.result?.secret) {\n setApiKey(res.result.secret)\n } else {\n setError(t('ai_assistant.mcp.error.failed'))\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : t('ai_assistant.mcp.error.failed'))\n } finally {\n setIsGenerating(false)\n }\n }\n\n const mcpConfig = {\n mcpServers: {\n 'open-mercato': {\n type: 'http',\n url: `${mcpUrl}/mcp`,\n headers: {\n 'x-api-key': apiKey || 'omk_xxxx.yyyy...',\n },\n },\n },\n }\n\n const configJson = JSON.stringify(mcpConfig, null, 2)\n\n const copyConfig = async () => {\n await navigator.clipboard.writeText(configJson)\n setCopiedConfig(true)\n setTimeout(() => setCopiedConfig(false), 2000)\n }\n\n const copyKey = async () => {\n if (apiKey) {\n await navigator.clipboard.writeText(apiKey)\n setCopiedKey(true)\n setTimeout(() => setCopiedKey(false), 2000)\n }\n }\n\n const handleClose = () => {\n setApiKey(null)\n setError(null)\n setCopiedConfig(false)\n setCopiedKey(false)\n onOpenChange(false)\n }\n\n const handleKeyDown = useDialogKeyHandler({ onCancel: handleClose })\n\n return (\n <Dialog open={open} onOpenChange={handleClose}>\n <DialogContent onKeyDown={handleKeyDown} className=\"sm:max-w-2xl\">\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <Settings className=\"h-5 w-5\" />\n {t('ai_assistant.mcp.title')}\n </DialogTitle>\n <DialogDescription>\n {t('ai_assistant.mcp.description')}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n {/* Config JSON */}\n <div className=\"relative\">\n <pre className=\"bg-muted/50 rounded-lg p-4 text-xs font-mono overflow-x-auto border\">\n {configJson}\n </pre>\n </div>\n\n {/* API Key Section */}\n <div className=\"flex items-center gap-3 flex-wrap\">\n <span className=\"text-sm text-muted-foreground\">{t('ai_assistant.mcp.apiKeyLabel')}</span>\n {apiKey ? (\n <>\n <code className=\"text-xs bg-muted px-2 py-1 rounded font-mono truncate max-w-[200px]\">\n {apiKey.slice(0, 16)}...\n </code>\n <Button variant=\"outline\" size=\"sm\" onClick={copyKey} className=\"gap-1.5\">\n {copiedKey ? <Check className=\"h-3.5 w-3.5\" /> : <Copy className=\"h-3.5 w-3.5\" />}\n {copiedKey ? t('ai_assistant.mcp.copied') : t('ai_assistant.mcp.copyKey')}\n </Button>\n </>\n ) : (\n <span className=\"text-sm text-muted-foreground italic\">{t('ai_assistant.mcp.notGenerated')}</span>\n )}\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={generateApiKey}\n disabled={isGenerating}\n className=\"gap-1.5\"\n >\n <RefreshCw className={`h-3.5 w-3.5 ${isGenerating ? 'animate-spin' : ''}`} />\n {apiKey ? t('ai_assistant.mcp.generateNew') : t('ai_assistant.mcp.generateApiKey')}\n </Button>\n </div>\n\n {error && (\n <div className=\"flex items-center gap-2 text-sm text-destructive\">\n <AlertTriangle className=\"h-4 w-4\" />\n {error}\n </div>\n )}\n\n {apiKey && (\n <div className=\"flex items-start gap-2 p-3 bg-status-warning-bg rounded-lg border border-status-warning-border\">\n <AlertTriangle className=\"h-4 w-4 text-status-warning-icon mt-0.5 shrink-0\" />\n <p className=\"text-sm text-status-warning-text\">\n {t('ai_assistant.mcp.saveKeyWarning')}\n </p>\n </div>\n )}\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={handleClose}>\n {t('ai_assistant.mcp.close')}\n </Button>\n <Button onClick={copyConfig} className=\"gap-1.5\">\n {copiedConfig ? <Check className=\"h-4 w-4\" /> : <Copy className=\"h-4 w-4\" />}\n {copiedConfig ? t('ai_assistant.mcp.copied') : t('ai_assistant.mcp.copyConfig')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useState } from 'react'\nimport { Copy, RefreshCw, AlertTriangle, Check, Settings } from 'lucide-react'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useDialogKeyHandler } from '@open-mercato/ui/hooks/useDialogKeyHandler'\n\ntype Props = {\n open: boolean\n onOpenChange: (next: boolean) => void\n mcpUrl: string\n}\n\ntype ApiKeyResponse = {\n id: string\n name: string\n keyPrefix: string\n secret?: string\n}\n\nexport default function McpConfigDialog({ open, onOpenChange, mcpUrl }: Props) {\n const t = useT()\n const [apiKey, setApiKey] = useState<string | null>(null)\n const [isGenerating, setIsGenerating] = useState(false)\n const [copiedConfig, setCopiedConfig] = useState(false)\n const [copiedKey, setCopiedKey] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const generateApiKey = async () => {\n setIsGenerating(true)\n setError(null)\n try {\n // Use the dedicated MCP-key endpoint so the key inherits the current\n // user's roles/ACL. A raw POST /api/api_keys/keys assigns no roles, which\n // produced a key with zero features (the MCP server then exposes only\n // `context_whoami`).\n const res = await apiCall<ApiKeyResponse>('/api/ai_assistant/mcp-key', {\n method: 'POST',\n body: JSON.stringify({\n name: `MCP Config - ${new Date().toLocaleDateString()}`,\n description: 'Generated from AI Assistant settings for MCP client',\n }),\n })\n if (res.ok && res.result?.secret) {\n setApiKey(res.result.secret)\n } else {\n setError(t('ai_assistant.mcp.error.failed'))\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : t('ai_assistant.mcp.error.failed'))\n } finally {\n setIsGenerating(false)\n }\n }\n\n const mcpConfig = {\n mcpServers: {\n 'open-mercato': {\n type: 'http',\n url: `${mcpUrl}/mcp`,\n headers: {\n 'x-api-key': apiKey || 'omk_xxxx.yyyy...',\n },\n },\n },\n }\n\n const configJson = JSON.stringify(mcpConfig, null, 2)\n\n const copyConfig = async () => {\n await navigator.clipboard.writeText(configJson)\n setCopiedConfig(true)\n setTimeout(() => setCopiedConfig(false), 2000)\n }\n\n const copyKey = async () => {\n if (apiKey) {\n await navigator.clipboard.writeText(apiKey)\n setCopiedKey(true)\n setTimeout(() => setCopiedKey(false), 2000)\n }\n }\n\n const handleClose = () => {\n setApiKey(null)\n setError(null)\n setCopiedConfig(false)\n setCopiedKey(false)\n onOpenChange(false)\n }\n\n const handleKeyDown = useDialogKeyHandler({ onCancel: handleClose })\n\n return (\n <Dialog open={open} onOpenChange={handleClose}>\n <DialogContent onKeyDown={handleKeyDown} className=\"sm:max-w-2xl\">\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <Settings className=\"h-5 w-5\" />\n {t('ai_assistant.mcp.title')}\n </DialogTitle>\n <DialogDescription>\n {t('ai_assistant.mcp.description')}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n {/* Config JSON */}\n <div className=\"relative\">\n <pre className=\"bg-muted/50 rounded-lg p-4 text-xs font-mono overflow-x-auto border\">\n {configJson}\n </pre>\n </div>\n\n {/* API Key Section */}\n <div className=\"flex items-center gap-3 flex-wrap\">\n <span className=\"text-sm text-muted-foreground\">{t('ai_assistant.mcp.apiKeyLabel')}</span>\n {apiKey ? (\n <>\n <code className=\"text-xs bg-muted px-2 py-1 rounded font-mono truncate max-w-[200px]\">\n {apiKey.slice(0, 16)}...\n </code>\n <Button variant=\"outline\" size=\"sm\" onClick={copyKey} className=\"gap-1.5\">\n {copiedKey ? <Check className=\"h-3.5 w-3.5\" /> : <Copy className=\"h-3.5 w-3.5\" />}\n {copiedKey ? t('ai_assistant.mcp.copied') : t('ai_assistant.mcp.copyKey')}\n </Button>\n </>\n ) : (\n <span className=\"text-sm text-muted-foreground italic\">{t('ai_assistant.mcp.notGenerated')}</span>\n )}\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={generateApiKey}\n disabled={isGenerating}\n className=\"gap-1.5\"\n >\n <RefreshCw className={`h-3.5 w-3.5 ${isGenerating ? 'animate-spin' : ''}`} />\n {apiKey ? t('ai_assistant.mcp.generateNew') : t('ai_assistant.mcp.generateApiKey')}\n </Button>\n </div>\n\n {error && (\n <div className=\"flex items-center gap-2 text-sm text-destructive\">\n <AlertTriangle className=\"h-4 w-4\" />\n {error}\n </div>\n )}\n\n {apiKey && (\n <div className=\"flex items-start gap-2 p-3 bg-status-warning-bg rounded-lg border border-status-warning-border\">\n <AlertTriangle className=\"h-4 w-4 text-status-warning-icon mt-0.5 shrink-0\" />\n <p className=\"text-sm text-status-warning-text\">\n {t('ai_assistant.mcp.saveKeyWarning')}\n </p>\n </div>\n )}\n </div>\n\n <DialogFooter>\n <Button variant=\"outline\" onClick={handleClose}>\n {t('ai_assistant.mcp.close')}\n </Button>\n <Button onClick={copyConfig} className=\"gap-1.5\">\n {copiedConfig ? <Check className=\"h-4 w-4\" /> : <Copy className=\"h-4 w-4\" />}\n {copiedConfig ? t('ai_assistant.mcp.copied') : t('ai_assistant.mcp.copyConfig')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -635,6 +635,10 @@ AiChatConversationParticipant = __decorateClass([
635
635
  name: "ai_chat_conv_participants_tenant_conv_user_null_org_uq",
636
636
  expression: 'create unique index "ai_chat_conv_participants_tenant_conv_user_null_org_uq" on "ai_chat_conversation_participants" ("tenant_id", "conversation_id", "user_id") where "organization_id" is null'
637
637
  }),
638
+ Index({
639
+ name: "ai_chat_conv_participants_active_conv_user_idx",
640
+ expression: 'create index "ai_chat_conv_participants_active_conv_user_idx" on "ai_chat_conversation_participants" ("tenant_id", "organization_id", "conversation_id", "user_id") where "deleted_at" is null'
641
+ }),
638
642
  Index({
639
643
  name: "ai_chat_conv_participants_tenant_org_user_conv_idx",
640
644
  properties: ["tenantId", "organizationId", "userId", "conversationId"]