@hed-hog/core 0.0.366 → 0.0.367

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 (52) hide show
  1. package/dist/integration/services/outbox-polling.coordinator.d.ts +2 -0
  2. package/dist/integration/services/outbox-polling.coordinator.d.ts.map +1 -1
  3. package/dist/integration/services/outbox-polling.coordinator.js +14 -4
  4. package/dist/integration/services/outbox-polling.coordinator.js.map +1 -1
  5. package/dist/integration/services/outbox.processor.d.ts.map +1 -1
  6. package/dist/integration/services/outbox.processor.js +4 -3
  7. package/dist/integration/services/outbox.processor.js.map +1 -1
  8. package/dist/locale/locale.controller.d.ts +1 -1
  9. package/dist/locale/locale.controller.d.ts.map +1 -1
  10. package/dist/locale/locale.controller.js +4 -3
  11. package/dist/locale/locale.controller.js.map +1 -1
  12. package/dist/locale/locale.service.d.ts +1 -1
  13. package/dist/locale/locale.service.d.ts.map +1 -1
  14. package/dist/locale/locale.service.js +6 -6
  15. package/dist/locale/locale.service.js.map +1 -1
  16. package/hedhog/data/integration_event_catalog.yaml +118 -118
  17. package/hedhog/data/integration_provider.yaml +25 -0
  18. package/hedhog/data/integration_type.yaml +6 -0
  19. package/hedhog/data/locale.yaml +5 -1
  20. package/hedhog/data/setting_group.yaml +102 -106
  21. package/hedhog/data/setting_subgroup.yaml +32 -0
  22. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +56 -0
  23. package/hedhog/frontend/messages/en.json +4 -0
  24. package/hedhog/frontend/messages/pt.json +4 -0
  25. package/hedhog/table/ai_instruction.yaml +1 -1
  26. package/hedhog/table/event_webhook.yaml +55 -55
  27. package/hedhog/table/event_webhook_delivery_log.yaml +66 -66
  28. package/hedhog/table/event_webhook_event.yaml +26 -26
  29. package/hedhog/table/integration_event_catalog.yaml +41 -41
  30. package/hedhog/table/mcp_conversation.yaml +12 -12
  31. package/hedhog/table/mcp_conversation_message.yaml +23 -23
  32. package/hedhog/table/mcp_tool_call_log.yaml +22 -22
  33. package/hedhog/table/route.yaml +33 -33
  34. package/hedhog/table/webhook_call_log.yaml +46 -46
  35. package/hedhog/table/webhook_integration.yaml +43 -43
  36. package/package.json +4 -4
  37. package/src/integration/services/outbox-polling.coordinator.ts +16 -4
  38. package/src/integration/services/outbox.processor.ts +14 -8
  39. package/src/integration/types/event.types.ts +4 -4
  40. package/src/locale/locale.controller.ts +2 -1
  41. package/src/locale/locale.service.ts +7 -6
  42. package/src/mcp/decorators/mcp-tool.decorator.ts +7 -7
  43. package/src/mcp/guards/mcp-permission.guard.ts +51 -51
  44. package/src/mcp/interfaces/mcp-tool-definition.interface.ts +6 -6
  45. package/src/mcp/mcp-server.service.ts +87 -87
  46. package/src/mcp/types/mcp-context.type.ts +6 -6
  47. package/src/mcp-chat/dto/create-conversation.dto.ts +7 -7
  48. package/src/mcp-chat/dto/send-message.dto.ts +7 -7
  49. package/src/route/dto/create.dto.ts +32 -32
  50. package/src/route/dto/update.dto.ts +31 -31
  51. package/src/webhook-integration/integration-event-catalog.controller.ts +50 -50
  52. package/src/webhook-integration/integration-event-catalog.service.ts +117 -117
@@ -1,7 +1,7 @@
1
- import { SetMetadata } from '@nestjs/common';
2
- import { McpToolDefinition } from '../interfaces/mcp-tool-definition.interface';
3
-
4
- export const MCP_TOOL_METADATA = 'mcp:tool';
5
-
6
- export const McpTool = (definition: McpToolDefinition): MethodDecorator =>
7
- SetMetadata(MCP_TOOL_METADATA, definition);
1
+ import { SetMetadata } from '@nestjs/common';
2
+ import { McpToolDefinition } from '../interfaces/mcp-tool-definition.interface';
3
+
4
+ export const MCP_TOOL_METADATA = 'mcp:tool';
5
+
6
+ export const McpTool = (definition: McpToolDefinition): MethodDecorator =>
7
+ SetMetadata(MCP_TOOL_METADATA, definition);
@@ -1,51 +1,51 @@
1
- import { PrismaService } from '@hed-hog/api-prisma';
2
- import {
3
- CanActivate,
4
- ExecutionContext,
5
- ForbiddenException,
6
- Injectable,
7
- } from '@nestjs/common';
8
- import { Request } from 'express';
9
-
10
- @Injectable()
11
- export class McpPermissionGuard implements CanActivate {
12
- constructor(private readonly prisma: PrismaService) {}
13
-
14
- async canActivate(context: ExecutionContext): Promise<boolean> {
15
- const request = context.switchToHttp().getRequest<Request>();
16
-
17
- // Only POST requests carry tool invocations
18
- if (request.method !== 'POST') return true;
19
-
20
- const body = request.body as any;
21
- const toolName: string | undefined =
22
- body?.method === 'tools/call' ? body?.params?.name : undefined;
23
-
24
- // tools/list, initialize, ping, etc. — no per-tool permission check needed
25
- if (!toolName) return true;
26
-
27
- const userId = (request as any)?.auth?.sub;
28
-
29
- const count = await this.prisma.route.count({
30
- where: {
31
- type: 'MCP' as any,
32
- tool_name: toolName,
33
- role_route: {
34
- some: {
35
- role: {
36
- role_user: {
37
- some: { user_id: userId },
38
- },
39
- },
40
- },
41
- },
42
- },
43
- });
44
-
45
- if (count === 0) {
46
- throw new ForbiddenException(`Access denied to MCP tool '${toolName}'`);
47
- }
48
-
49
- return true;
50
- }
51
- }
1
+ import { PrismaService } from '@hed-hog/api-prisma';
2
+ import {
3
+ CanActivate,
4
+ ExecutionContext,
5
+ ForbiddenException,
6
+ Injectable,
7
+ } from '@nestjs/common';
8
+ import { Request } from 'express';
9
+
10
+ @Injectable()
11
+ export class McpPermissionGuard implements CanActivate {
12
+ constructor(private readonly prisma: PrismaService) {}
13
+
14
+ async canActivate(context: ExecutionContext): Promise<boolean> {
15
+ const request = context.switchToHttp().getRequest<Request>();
16
+
17
+ // Only POST requests carry tool invocations
18
+ if (request.method !== 'POST') return true;
19
+
20
+ const body = request.body as any;
21
+ const toolName: string | undefined =
22
+ body?.method === 'tools/call' ? body?.params?.name : undefined;
23
+
24
+ // tools/list, initialize, ping, etc. — no per-tool permission check needed
25
+ if (!toolName) return true;
26
+
27
+ const userId = (request as any)?.auth?.sub;
28
+
29
+ const count = await this.prisma.route.count({
30
+ where: {
31
+ type: 'MCP' as any,
32
+ tool_name: toolName,
33
+ role_route: {
34
+ some: {
35
+ role: {
36
+ role_user: {
37
+ some: { user_id: userId },
38
+ },
39
+ },
40
+ },
41
+ },
42
+ },
43
+ });
44
+
45
+ if (count === 0) {
46
+ throw new ForbiddenException(`Access denied to MCP tool '${toolName}'`);
47
+ }
48
+
49
+ return true;
50
+ }
51
+ }
@@ -1,6 +1,6 @@
1
- export interface McpToolDefinition {
2
- name: string;
3
- description: string;
4
- inputSchema?: Record<string, any>;
5
- readOnly?: boolean;
6
- }
1
+ export interface McpToolDefinition {
2
+ name: string;
3
+ description: string;
4
+ inputSchema?: Record<string, any>;
5
+ readOnly?: boolean;
6
+ }
@@ -1,87 +1,87 @@
1
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
- import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
- import {
4
- CallToolRequestSchema,
5
- ListToolsRequestSchema,
6
- } from '@modelcontextprotocol/sdk/types.js';
7
- import { Injectable } from '@nestjs/common';
8
- import { Request, Response } from 'express';
9
- import { McpAuditService } from './mcp-audit.service';
10
- import { McpRegistryService } from './mcp-registry.service';
11
- import { McpContext } from './types/mcp-context.type';
12
-
13
- @Injectable()
14
- export class McpServerService {
15
- constructor(
16
- private readonly registry: McpRegistryService,
17
- private readonly audit: McpAuditService,
18
- ) {}
19
-
20
- async handleRequest(req: Request, res: Response, context: McpContext): Promise<void> {
21
- const server = this.buildServer(context);
22
- const transport = new StreamableHTTPServerTransport({
23
- sessionIdGenerator: undefined,
24
- });
25
- await server.connect(transport);
26
- await transport.handleRequest(req, res, req.body);
27
- }
28
-
29
- private buildServer(context: McpContext): Server {
30
- const server = new Server(
31
- { name: 'hedhog-mcp', version: '1.0.0' },
32
- { capabilities: { tools: {} } },
33
- );
34
-
35
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
36
- tools: this.registry.getAll().map((e) => ({
37
- name: e.definition.name,
38
- description: e.definition.description,
39
- inputSchema: e.definition.inputSchema ?? {
40
- type: 'object',
41
- properties: {},
42
- },
43
- })),
44
- }));
45
-
46
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
47
- const toolName = request.params.name;
48
- const args = (request.params.arguments ?? {}) as Record<string, any>;
49
- const entry = this.registry.get(toolName);
50
-
51
- if (!entry) {
52
- return {
53
- isError: true,
54
- content: [{ type: 'text', text: `Tool '${toolName}' not found` }],
55
- };
56
- }
57
-
58
- const start = Date.now();
59
- try {
60
- const result = await entry.handler(args, { ...context, toolName });
61
- await this.audit.record({
62
- userId: context.userId,
63
- toolName,
64
- input: args,
65
- success: true,
66
- durationMs: Date.now() - start,
67
- });
68
- return { content: [{ type: 'text', text: JSON.stringify(result) }] };
69
- } catch (e: any) {
70
- await this.audit.record({
71
- userId: context.userId,
72
- toolName,
73
- input: args,
74
- success: false,
75
- error: e.message,
76
- durationMs: Date.now() - start,
77
- });
78
- return {
79
- isError: true,
80
- content: [{ type: 'text', text: e.message }],
81
- };
82
- }
83
- });
84
-
85
- return server;
86
- }
87
- }
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
+ import {
4
+ CallToolRequestSchema,
5
+ ListToolsRequestSchema,
6
+ } from '@modelcontextprotocol/sdk/types.js';
7
+ import { Injectable } from '@nestjs/common';
8
+ import { Request, Response } from 'express';
9
+ import { McpAuditService } from './mcp-audit.service';
10
+ import { McpRegistryService } from './mcp-registry.service';
11
+ import { McpContext } from './types/mcp-context.type';
12
+
13
+ @Injectable()
14
+ export class McpServerService {
15
+ constructor(
16
+ private readonly registry: McpRegistryService,
17
+ private readonly audit: McpAuditService,
18
+ ) {}
19
+
20
+ async handleRequest(req: Request, res: Response, context: McpContext): Promise<void> {
21
+ const server = this.buildServer(context);
22
+ const transport = new StreamableHTTPServerTransport({
23
+ sessionIdGenerator: undefined,
24
+ });
25
+ await server.connect(transport);
26
+ await transport.handleRequest(req, res, req.body);
27
+ }
28
+
29
+ private buildServer(context: McpContext): Server {
30
+ const server = new Server(
31
+ { name: 'hedhog-mcp', version: '1.0.0' },
32
+ { capabilities: { tools: {} } },
33
+ );
34
+
35
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
36
+ tools: this.registry.getAll().map((e) => ({
37
+ name: e.definition.name,
38
+ description: e.definition.description,
39
+ inputSchema: e.definition.inputSchema ?? {
40
+ type: 'object',
41
+ properties: {},
42
+ },
43
+ })),
44
+ }));
45
+
46
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
47
+ const toolName = request.params.name;
48
+ const args = (request.params.arguments ?? {}) as Record<string, any>;
49
+ const entry = this.registry.get(toolName);
50
+
51
+ if (!entry) {
52
+ return {
53
+ isError: true,
54
+ content: [{ type: 'text', text: `Tool '${toolName}' not found` }],
55
+ };
56
+ }
57
+
58
+ const start = Date.now();
59
+ try {
60
+ const result = await entry.handler(args, { ...context, toolName });
61
+ await this.audit.record({
62
+ userId: context.userId,
63
+ toolName,
64
+ input: args,
65
+ success: true,
66
+ durationMs: Date.now() - start,
67
+ });
68
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
69
+ } catch (e: any) {
70
+ await this.audit.record({
71
+ userId: context.userId,
72
+ toolName,
73
+ input: args,
74
+ success: false,
75
+ error: e.message,
76
+ durationMs: Date.now() - start,
77
+ });
78
+ return {
79
+ isError: true,
80
+ content: [{ type: 'text', text: e.message }],
81
+ };
82
+ }
83
+ });
84
+
85
+ return server;
86
+ }
87
+ }
@@ -1,6 +1,6 @@
1
- export type McpContext = {
2
- userId: number;
3
- sessionId: string;
4
- locale: string;
5
- toolName: string;
6
- };
1
+ export type McpContext = {
2
+ userId: number;
3
+ sessionId: string;
4
+ locale: string;
5
+ toolName: string;
6
+ };
@@ -1,7 +1,7 @@
1
- import { IsOptional, IsString } from 'class-validator';
2
-
3
- export class CreateConversationDTO {
4
- @IsOptional()
5
- @IsString()
6
- title?: string;
7
- }
1
+ import { IsOptional, IsString } from 'class-validator';
2
+
3
+ export class CreateConversationDTO {
4
+ @IsOptional()
5
+ @IsString()
6
+ title?: string;
7
+ }
@@ -1,7 +1,7 @@
1
- import { IsNotEmpty, IsString } from 'class-validator';
2
-
3
- export class SendMessageDTO {
4
- @IsString()
5
- @IsNotEmpty()
6
- message: string;
7
- }
1
+ import { IsNotEmpty, IsString } from 'class-validator';
2
+
3
+ export class SendMessageDTO {
4
+ @IsString()
5
+ @IsNotEmpty()
6
+ message: string;
7
+ }
@@ -1,32 +1,32 @@
1
- import { getLocaleText } from '@hed-hog/api-locale';
2
- import { IsIn, IsOptional, IsString } from 'class-validator';
3
- import { HttpMethod } from '../../types/http-method';
4
-
5
- export type RouteType = 'HTTP' | 'MCP';
6
-
7
- const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'];
8
-
9
- export class CreateDTO {
10
- @IsOptional()
11
- @IsString({ message: (args) => getLocaleText('validation.urlMustBeString', args.value) })
12
- url?: string;
13
-
14
- @IsOptional()
15
- @IsString({ message: (args) => getLocaleText('validation.methodMustBeString', args.value) })
16
- @IsIn(HTTP_METHODS, {
17
- message: (args) => getLocaleText('validation.methodMustBeValid', args.value),
18
- })
19
- method?: HttpMethod;
20
-
21
- @IsOptional()
22
- @IsIn(['HTTP', 'MCP'])
23
- type?: RouteType;
24
-
25
- @IsOptional()
26
- @IsString()
27
- tool_name?: string;
28
-
29
- @IsOptional()
30
- @IsString()
31
- name?: string;
32
- }
1
+ import { getLocaleText } from '@hed-hog/api-locale';
2
+ import { IsIn, IsOptional, IsString } from 'class-validator';
3
+ import { HttpMethod } from '../../types/http-method';
4
+
5
+ export type RouteType = 'HTTP' | 'MCP';
6
+
7
+ const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'];
8
+
9
+ export class CreateDTO {
10
+ @IsOptional()
11
+ @IsString({ message: (args) => getLocaleText('validation.urlMustBeString', args.value) })
12
+ url?: string;
13
+
14
+ @IsOptional()
15
+ @IsString({ message: (args) => getLocaleText('validation.methodMustBeString', args.value) })
16
+ @IsIn(HTTP_METHODS, {
17
+ message: (args) => getLocaleText('validation.methodMustBeValid', args.value),
18
+ })
19
+ method?: HttpMethod;
20
+
21
+ @IsOptional()
22
+ @IsIn(['HTTP', 'MCP'])
23
+ type?: RouteType;
24
+
25
+ @IsOptional()
26
+ @IsString()
27
+ tool_name?: string;
28
+
29
+ @IsOptional()
30
+ @IsString()
31
+ name?: string;
32
+ }
@@ -1,31 +1,31 @@
1
- import { getLocaleText } from '@hed-hog/api-locale';
2
- import { IsIn, IsOptional, IsString } from 'class-validator';
3
- import { HttpMethod } from '../../types/http-method';
4
- import { RouteType } from './create.dto';
5
-
6
- const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'];
7
-
8
- export class UpdateDTO {
9
- @IsOptional()
10
- @IsString({ message: (args) => getLocaleText('validation.urlMustBeString', args.value) })
11
- url?: string;
12
-
13
- @IsOptional()
14
- @IsString({ message: (args) => getLocaleText('validation.methodMustBeString', args.value) })
15
- @IsIn(HTTP_METHODS, {
16
- message: (args) => getLocaleText('validation.methodMustBeValid', args.value),
17
- })
18
- method?: HttpMethod;
19
-
20
- @IsOptional()
21
- @IsIn(['HTTP', 'MCP'])
22
- type?: RouteType;
23
-
24
- @IsOptional()
25
- @IsString()
26
- tool_name?: string;
27
-
28
- @IsOptional()
29
- @IsString()
30
- name?: string;
31
- }
1
+ import { getLocaleText } from '@hed-hog/api-locale';
2
+ import { IsIn, IsOptional, IsString } from 'class-validator';
3
+ import { HttpMethod } from '../../types/http-method';
4
+ import { RouteType } from './create.dto';
5
+
6
+ const HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'];
7
+
8
+ export class UpdateDTO {
9
+ @IsOptional()
10
+ @IsString({ message: (args) => getLocaleText('validation.urlMustBeString', args.value) })
11
+ url?: string;
12
+
13
+ @IsOptional()
14
+ @IsString({ message: (args) => getLocaleText('validation.methodMustBeString', args.value) })
15
+ @IsIn(HTTP_METHODS, {
16
+ message: (args) => getLocaleText('validation.methodMustBeValid', args.value),
17
+ })
18
+ method?: HttpMethod;
19
+
20
+ @IsOptional()
21
+ @IsIn(['HTTP', 'MCP'])
22
+ type?: RouteType;
23
+
24
+ @IsOptional()
25
+ @IsString()
26
+ tool_name?: string;
27
+
28
+ @IsOptional()
29
+ @IsString()
30
+ name?: string;
31
+ }
@@ -1,50 +1,50 @@
1
- import { DeleteDTO, Role } from '@hed-hog/api';
2
- import { Pagination } from '@hed-hog/api-pagination';
3
- import {
4
- Body,
5
- Controller,
6
- Delete,
7
- Get,
8
- Param,
9
- ParseIntPipe,
10
- Patch,
11
- Post,
12
- } from '@nestjs/common';
13
- import { IntegrationEventCatalogService } from './integration-event-catalog.service';
14
-
15
- @Role()
16
- @Controller('integration-event-catalog')
17
- export class IntegrationEventCatalogController {
18
- constructor(private readonly service: IntegrationEventCatalogService) {}
19
-
20
- @Get()
21
- list(@Pagination() paginationParams) {
22
- return this.service.list(paginationParams);
23
- }
24
-
25
- @Get('options')
26
- options() {
27
- return this.service.options();
28
- }
29
-
30
- @Get(':id')
31
- get(@Param('id', ParseIntPipe) id: number) {
32
- return this.service.get(id);
33
- }
34
-
35
- @Post()
36
- create(@Body() data: any) {
37
- return this.service.create(data);
38
- }
39
-
40
- @Patch(':id')
41
- update(@Param('id', ParseIntPipe) id: number, @Body() data: any) {
42
- return this.service.update(id, data);
43
- }
44
-
45
- @Delete()
46
- delete(@Body() data: DeleteDTO) {
47
- return this.service.delete(data);
48
- }
49
- }
50
-
1
+ import { DeleteDTO, Role } from '@hed-hog/api';
2
+ import { Pagination } from '@hed-hog/api-pagination';
3
+ import {
4
+ Body,
5
+ Controller,
6
+ Delete,
7
+ Get,
8
+ Param,
9
+ ParseIntPipe,
10
+ Patch,
11
+ Post,
12
+ } from '@nestjs/common';
13
+ import { IntegrationEventCatalogService } from './integration-event-catalog.service';
14
+
15
+ @Role()
16
+ @Controller('integration-event-catalog')
17
+ export class IntegrationEventCatalogController {
18
+ constructor(private readonly service: IntegrationEventCatalogService) {}
19
+
20
+ @Get()
21
+ list(@Pagination() paginationParams) {
22
+ return this.service.list(paginationParams);
23
+ }
24
+
25
+ @Get('options')
26
+ options() {
27
+ return this.service.options();
28
+ }
29
+
30
+ @Get(':id')
31
+ get(@Param('id', ParseIntPipe) id: number) {
32
+ return this.service.get(id);
33
+ }
34
+
35
+ @Post()
36
+ create(@Body() data: any) {
37
+ return this.service.create(data);
38
+ }
39
+
40
+ @Patch(':id')
41
+ update(@Param('id', ParseIntPipe) id: number, @Body() data: any) {
42
+ return this.service.update(id, data);
43
+ }
44
+
45
+ @Delete()
46
+ delete(@Body() data: DeleteDTO) {
47
+ return this.service.delete(data);
48
+ }
49
+ }
50
+