@pikku/core 0.7.8 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/dist/errors/error-handler.d.ts +2 -0
  3. package/dist/errors/errors.js +2 -0
  4. package/dist/{channel → events/channel}/channel-handler.d.ts +1 -1
  5. package/dist/{channel → events/channel}/channel-handler.js +2 -2
  6. package/dist/{channel → events/channel}/channel-runner.d.ts +1 -1
  7. package/dist/{channel → events/channel}/channel-runner.js +4 -4
  8. package/dist/{channel → events/channel}/channel-store.d.ts +1 -1
  9. package/dist/{channel → events/channel}/channel.types.d.ts +3 -3
  10. package/dist/{channel → events/channel}/local/local-channel-runner.js +6 -6
  11. package/dist/{channel → events/channel}/log-channels.d.ts +1 -1
  12. package/dist/{channel → events/channel}/log-channels.js +1 -1
  13. package/dist/{channel → events/channel}/serverless/serverless-channel-runner.js +7 -7
  14. package/dist/{http → events/http}/http-runner.d.ts +1 -1
  15. package/dist/{http → events/http}/http-runner.js +8 -8
  16. package/dist/{http → events/http}/http.types.d.ts +3 -3
  17. package/dist/{http → events/http}/index.d.ts +0 -1
  18. package/dist/{http → events/http}/index.js +0 -1
  19. package/dist/{http → events/http}/log-http-routes.d.ts +1 -1
  20. package/dist/{http → events/http}/log-http-routes.js +1 -1
  21. package/dist/{http → events/http}/pikku-fetch-http-request.js +1 -1
  22. package/dist/events/mcp/index.d.ts +2 -0
  23. package/dist/events/mcp/index.js +2 -0
  24. package/dist/events/mcp/mcp-runner.d.ts +30 -0
  25. package/dist/events/mcp/mcp-runner.js +192 -0
  26. package/dist/events/mcp/mcp.types.d.ts +117 -0
  27. package/dist/events/queue/index.d.ts +6 -0
  28. package/dist/events/queue/index.js +6 -0
  29. package/dist/events/queue/queue-runner.d.ts +24 -0
  30. package/dist/events/queue/queue-runner.js +83 -0
  31. package/dist/events/queue/queue.types.d.ts +154 -0
  32. package/dist/events/queue/register-queue-helper.d.ts +17 -0
  33. package/dist/events/queue/register-queue-helper.js +34 -0
  34. package/dist/events/queue/validate-worker-config.d.ts +41 -0
  35. package/dist/events/queue/validate-worker-config.js +50 -0
  36. package/dist/events/rpc/index.d.ts +2 -0
  37. package/dist/events/rpc/rpc-runner.d.ts +28 -0
  38. package/dist/{rpc → events/rpc}/rpc-runner.js +2 -2
  39. package/dist/events/scheduler/index.d.ts +3 -0
  40. package/dist/events/scheduler/index.js +3 -0
  41. package/dist/{scheduler → events/scheduler}/log-schedulers.d.ts +1 -1
  42. package/dist/{scheduler → events/scheduler}/log-schedulers.js +1 -1
  43. package/dist/{scheduler → events/scheduler}/scheduler-runner.d.ts +4 -3
  44. package/dist/{scheduler → events/scheduler}/scheduler-runner.js +7 -4
  45. package/dist/{scheduler → events/scheduler}/scheduler.types.d.ts +3 -4
  46. package/dist/function/function-runner.js +8 -0
  47. package/dist/function/functions.types.d.ts +3 -2
  48. package/dist/handle-error.d.ts +2 -2
  49. package/dist/handle-error.js +1 -1
  50. package/dist/index.d.ts +10 -6
  51. package/dist/index.js +10 -6
  52. package/dist/middleware-runner.d.ts +4 -4
  53. package/dist/middleware-runner.js +3 -1
  54. package/dist/pikku-state.d.ts +18 -4
  55. package/dist/pikku-state.js +12 -0
  56. package/dist/services/user-session-service.d.ts +1 -1
  57. package/dist/types/core.types.d.ts +17 -5
  58. package/dist/types/core.types.js +2 -0
  59. package/lcov.info +3191 -3123
  60. package/package.json +8 -7
  61. package/src/errors/error-handler.ts +2 -1
  62. package/src/errors/errors.ts +2 -0
  63. package/src/{channel → events/channel}/channel-handler.ts +3 -3
  64. package/src/{channel → events/channel}/channel-runner.ts +5 -5
  65. package/src/{channel → events/channel}/channel-store.ts +1 -1
  66. package/src/{channel → events/channel}/channel.types.ts +3 -3
  67. package/src/{channel → events/channel}/local/local-channel-runner.test.ts +1 -1
  68. package/src/{channel → events/channel}/local/local-channel-runner.ts +7 -7
  69. package/src/{channel → events/channel}/local/local-eventhub-service.test.ts +1 -1
  70. package/src/{channel → events/channel}/log-channels.ts +2 -2
  71. package/src/{channel → events/channel}/serverless/serverless-channel-runner.ts +8 -8
  72. package/src/{http → events/http}/http-runner.test.ts +4 -4
  73. package/src/{http → events/http}/http-runner.ts +9 -9
  74. package/src/{http → events/http}/http.types.ts +3 -3
  75. package/src/{http → events/http}/index.ts +0 -1
  76. package/src/{http → events/http}/log-http-routes.ts +2 -2
  77. package/src/{http → events/http}/pikku-fetch-http-request.ts +1 -1
  78. package/src/events/mcp/index.ts +2 -0
  79. package/src/events/mcp/mcp-runner.ts +321 -0
  80. package/src/events/mcp/mcp.types.ts +150 -0
  81. package/src/events/queue/index.ts +18 -0
  82. package/src/events/queue/queue-runner.ts +125 -0
  83. package/src/events/queue/queue.types.ts +176 -0
  84. package/src/events/queue/register-queue-helper.ts +62 -0
  85. package/src/events/queue/validate-worker-config.ts +113 -0
  86. package/src/events/rpc/index.ts +2 -0
  87. package/src/{rpc → events/rpc}/rpc-runner.ts +3 -3
  88. package/src/events/scheduler/index.ts +9 -0
  89. package/src/{scheduler → events/scheduler}/log-schedulers.ts +2 -2
  90. package/src/{scheduler → events/scheduler}/scheduler-runner.ts +11 -10
  91. package/src/{scheduler → events/scheduler}/scheduler.types.ts +2 -4
  92. package/src/function/function-runner.ts +19 -1
  93. package/src/function/functions.types.ts +3 -1
  94. package/src/handle-error.ts +2 -2
  95. package/src/index.ts +23 -6
  96. package/src/middleware-runner.ts +14 -7
  97. package/src/pikku-state.ts +40 -4
  98. package/src/services/user-session-service.ts +1 -1
  99. package/src/types/core.types.ts +24 -4
  100. package/tsconfig.json +3 -2
  101. package/tsconfig.tsbuildinfo +1 -1
  102. package/dist/rpc/index.d.ts +0 -2
  103. package/dist/rpc/rpc-runner.d.ts +0 -27
  104. package/dist/scheduler/index.d.ts +0 -3
  105. package/dist/scheduler/index.js +0 -3
  106. package/src/http/incomingmessage-to-request-convertor.ts +0 -0
  107. package/src/rpc/index.ts +0 -2
  108. package/src/scheduler/index.ts +0 -5
  109. /package/dist/{channel → events/channel}/channel-store.js +0 -0
  110. /package/dist/{channel → events/channel}/channel.types.js +0 -0
  111. /package/dist/{channel → events/channel}/eventhub-service.d.ts +0 -0
  112. /package/dist/{channel → events/channel}/eventhub-service.js +0 -0
  113. /package/dist/{channel → events/channel}/eventhub-store.d.ts +0 -0
  114. /package/dist/{channel → events/channel}/eventhub-store.js +0 -0
  115. /package/dist/{channel → events/channel}/index.d.ts +0 -0
  116. /package/dist/{channel → events/channel}/index.js +0 -0
  117. /package/dist/{channel → events/channel}/local/index.d.ts +0 -0
  118. /package/dist/{channel → events/channel}/local/index.js +0 -0
  119. /package/dist/{channel → events/channel}/local/local-channel-handler.d.ts +0 -0
  120. /package/dist/{channel → events/channel}/local/local-channel-handler.js +0 -0
  121. /package/dist/{channel → events/channel}/local/local-channel-runner.d.ts +0 -0
  122. /package/dist/{channel → events/channel}/local/local-eventhub-service.d.ts +0 -0
  123. /package/dist/{channel → events/channel}/local/local-eventhub-service.js +0 -0
  124. /package/dist/{channel → events/channel}/pikku-abstract-channel-handler.d.ts +0 -0
  125. /package/dist/{channel → events/channel}/pikku-abstract-channel-handler.js +0 -0
  126. /package/dist/{channel → events/channel}/serverless/index.d.ts +0 -0
  127. /package/dist/{channel → events/channel}/serverless/index.js +0 -0
  128. /package/dist/{channel → events/channel}/serverless/serverless-channel-runner.d.ts +0 -0
  129. /package/dist/{http → events/http}/http.types.js +0 -0
  130. /package/dist/{http → events/http}/pikku-fetch-http-request.d.ts +0 -0
  131. /package/dist/{http → events/http}/pikku-fetch-http-response.d.ts +0 -0
  132. /package/dist/{http → events/http}/pikku-fetch-http-response.js +0 -0
  133. /package/dist/{http/incomingmessage-to-request-convertor.d.ts → events/mcp/mcp.types.js} +0 -0
  134. /package/dist/{http/incomingmessage-to-request-convertor.js → events/queue/queue.types.js} +0 -0
  135. /package/dist/{rpc → events/rpc}/index.js +0 -0
  136. /package/dist/{rpc → events/rpc}/rpc-types.d.ts +0 -0
  137. /package/dist/{rpc → events/rpc}/rpc-types.js +0 -0
  138. /package/dist/{scheduler → events/scheduler}/scheduler.types.js +0 -0
  139. /package/src/{channel → events/channel}/eventhub-service.ts +0 -0
  140. /package/src/{channel → events/channel}/eventhub-store.ts +0 -0
  141. /package/src/{channel → events/channel}/index.ts +0 -0
  142. /package/src/{channel → events/channel}/local/index.ts +0 -0
  143. /package/src/{channel → events/channel}/local/local-channel-handler.ts +0 -0
  144. /package/src/{channel → events/channel}/local/local-eventhub-service.ts +0 -0
  145. /package/src/{channel → events/channel}/pikku-abstract-channel-handler.test.ts +0 -0
  146. /package/src/{channel → events/channel}/pikku-abstract-channel-handler.ts +0 -0
  147. /package/src/{channel → events/channel}/serverless/index.ts +0 -0
  148. /package/src/{http → events/http}/pikku-fetch-http-request.test.ts +0 -0
  149. /package/src/{http → events/http}/pikku-fetch-http-response.test.ts +0 -0
  150. /package/src/{http → events/http}/pikku-fetch-http-response.ts +0 -0
  151. /package/src/{rpc → events/rpc}/rpc-types.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/core",
3
- "version": "0.7.8",
3
+ "version": "0.8.0",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "module": "dist/index.js",
@@ -20,13 +20,14 @@
20
20
  ".": "./dist/index.js",
21
21
  "./middleware": "./dist/middleware/index.js",
22
22
  "./function": "./dist/function/index.js",
23
- "./channel": "./dist/channel/index.js",
24
- "./channel/local": "./dist/channel/local/index.js",
25
- "./channel/serverless": "./dist/channel/serverless/index.js",
26
- "./http": "./dist/http/index.js",
27
- "./rpc": "./dist/rpc/index.js",
23
+ "./channel": "./dist/events/channel/index.js",
24
+ "./channel/local": "./dist/events/channel/local/index.js",
25
+ "./channel/serverless": "./dist/events/channel/serverless/index.js",
26
+ "./http": "./dist/events/http/index.js",
27
+ "./queue": "./dist/events/queue/index.js",
28
+ "./scheduler": "./dist/events/scheduler/index.js",
29
+ "./rpc": "./dist/events/rpc/index.js",
28
30
  "./errors": "./dist/errors/index.js",
29
- "./scheduler": "./dist/scheduler/index.js",
30
31
  "./services": "./dist/services/index.js",
31
32
  "./schema": "./dist/schema.js",
32
33
  "./types": "./dist/types/index.d.ts"
@@ -21,6 +21,7 @@ export class PikkuError extends Error {
21
21
  export interface ErrorDetails {
22
22
  status: number
23
23
  message: string
24
+ mcpCode?: number
24
25
  }
25
26
 
26
27
  /**
@@ -51,7 +52,7 @@ export const addErrors = (
51
52
  */
52
53
  export const getErrorResponse = (
53
54
  error: Error
54
- ): { status: number; message: string } | undefined => {
55
+ ): { status: number; message: string; mcpCode?: number } | undefined => {
55
56
  const errors = Array.from(pikkuState('misc', 'errors').entries())
56
57
  const foundError = errors.find(([e]) => e.name === error.constructor.name)
57
58
  if (foundError) {
@@ -10,6 +10,7 @@ import { addError, PikkuError } from './error-handler.js'
10
10
  export class BadRequestError extends PikkuError {}
11
11
  addError(BadRequestError, {
12
12
  status: 400,
13
+ mcpCode: -32600,
13
14
  message:
14
15
  'The server cannot or will not process the request due to client error (e.g., malformed request syntax).',
15
16
  })
@@ -85,6 +86,7 @@ export class NotFoundError extends PikkuError {}
85
86
  */
86
87
  addError(NotFoundError, {
87
88
  status: 404,
89
+ mcpCode: -32601,
88
90
  message: 'The server cannot find the requested resource.',
89
91
  })
90
92
 
@@ -2,14 +2,14 @@ import {
2
2
  CoreServices,
3
3
  JSONValue,
4
4
  CoreUserSession,
5
- } from '../types/core.types.js'
5
+ } from '../../types/core.types.js'
6
6
  import {
7
7
  ChannelMessageMeta,
8
8
  CoreAPIChannel,
9
9
  PikkuChannelHandler,
10
10
  } from './channel.types.js'
11
- import { pikkuState } from '../pikku-state.js'
12
- import { runPikkuFunc } from '../function/function-runner.js'
11
+ import { pikkuState } from '../../pikku-state.js'
12
+ import { runPikkuFunc } from '../../function/function-runner.js'
13
13
 
14
14
  const getRouteMeta = (
15
15
  channelName: string,
@@ -1,8 +1,8 @@
1
- import { NotFoundError } from '../errors/errors.js'
2
- import { addFunction } from '../function/function-runner.js'
3
- import { pikkuState } from '../pikku-state.js'
4
- import { coerceTopLevelDataFromSchema, validateSchema } from '../schema.js'
5
- import { UserSessionService } from '../services/user-session-service.js'
1
+ import { NotFoundError } from '../../errors/errors.js'
2
+ import { addFunction } from '../../function/function-runner.js'
3
+ import { pikkuState } from '../../pikku-state.js'
4
+ import { coerceTopLevelDataFromSchema, validateSchema } from '../../schema.js'
5
+ import { UserSessionService } from '../../services/user-session-service.js'
6
6
  import {
7
7
  ChannelMeta,
8
8
  CoreAPIChannel,
@@ -1,4 +1,4 @@
1
- import { CoreUserSession } from '../types/core.types.js'
1
+ import { CoreUserSession } from '../../types/core.types.js'
2
2
 
3
3
  export type Channel<ChannelType = unknown, OpeningData = unknown> = {
4
4
  channelId: string
@@ -1,4 +1,4 @@
1
- import { PikkuError } from '../errors/error-handler.js'
1
+ import { PikkuError } from '../../errors/error-handler.js'
2
2
  import {
3
3
  HTTPFunctionMetaInputTypes,
4
4
  PikkuHTTPRequest,
@@ -9,12 +9,12 @@ import {
9
9
  CoreSingletonServices,
10
10
  CreateSessionServices,
11
11
  PikkuMiddleware,
12
- } from '../types/core.types.js'
12
+ } from '../../types/core.types.js'
13
13
  import {
14
14
  CoreAPIFunction,
15
15
  CoreAPIFunctionSessionless,
16
16
  CoreAPIPermission,
17
- } from '../function/functions.types.js'
17
+ } from '../../function/functions.types.js'
18
18
 
19
19
  export type RunChannelOptions = Partial<{
20
20
  skipUserSession: boolean
@@ -1,7 +1,7 @@
1
1
  import { test, beforeEach, afterEach } from 'node:test'
2
2
  import * as assert from 'node:assert/strict'
3
3
  import { runLocalChannel } from './local-channel-runner.js'
4
- import { pikkuState, resetPikkuState } from '../../pikku-state.js'
4
+ import { pikkuState, resetPikkuState } from '../../../pikku-state.js'
5
5
  import { addChannel } from '../channel-runner.js'
6
6
  import {
7
7
  HTTPMethod,
@@ -1,6 +1,6 @@
1
1
  import { openChannel } from '../channel-runner.js'
2
2
  import { createHTTPInteraction } from '../../http/http-runner.js'
3
- import { closeSessionServices } from '../../utils.js'
3
+ import { closeSessionServices } from '../../../utils.js'
4
4
  import { processMessageHandlers } from '../channel-handler.js'
5
5
  import {
6
6
  CoreAPIChannel,
@@ -8,12 +8,12 @@ import {
8
8
  RunChannelParams,
9
9
  } from '../channel.types.js'
10
10
  import { PikkuLocalChannelHandler } from './local-channel-handler.js'
11
- import { SessionServices } from '../../types/core.types.js'
12
- import { handleError } from '../../handle-error.js'
13
- import { runMiddleware } from '../../middleware-runner.js'
14
- import { PikkuUserSessionService } from '../../services/user-session-service.js'
11
+ import { SessionServices } from '../../../types/core.types.js'
12
+ import { handleHTTPError } from '../../../handle-error.js'
13
+ import { runMiddleware } from '../../../middleware-runner.js'
14
+ import { PikkuUserSessionService } from '../../../services/user-session-service.js'
15
15
  import { PikkuHTTP } from '../../http/http.types.js'
16
- import { runPikkuFuncDirectly } from '../../function/function-runner.js'
16
+ import { runPikkuFuncDirectly } from '../../../function/function-runner.js'
17
17
  import { rpcService } from '../../rpc/rpc-runner.js'
18
18
 
19
19
  export const runLocalChannel = async ({
@@ -110,7 +110,7 @@ export const runLocalChannel = async ({
110
110
  )
111
111
  )
112
112
  } catch (e: any) {
113
- handleError(
113
+ handleHTTPError(
114
114
  e,
115
115
  http,
116
116
  channelId,
@@ -1,7 +1,7 @@
1
1
  import * as assert from 'assert'
2
2
  import { test } from 'node:test'
3
3
  import { PikkuChannelHandler } from '../channel.types.js'
4
- import { CoreUserSession } from '../../types/core.types.js'
4
+ import { CoreUserSession } from '../../../types/core.types.js'
5
5
  import { LocalEventHubService } from './local-eventhub-service.js'
6
6
 
7
7
  class MockChannelHandler implements PikkuChannelHandler {
@@ -1,5 +1,5 @@
1
- import { pikkuState } from '../pikku-state.js'
2
- import { Logger } from '../services/index.js'
1
+ import { pikkuState } from '../../pikku-state.js'
2
+ import { Logger } from '../../services/index.js'
3
3
 
4
4
  /**
5
5
  * Logs all the loaded channels.
@@ -1,5 +1,5 @@
1
- import { SessionServices } from '../../types/core.types.js'
2
- import { closeSessionServices } from '../../utils.js'
1
+ import { SessionServices } from '../../../types/core.types.js'
2
+ import { closeSessionServices } from '../../../utils.js'
3
3
  import { processMessageHandlers } from '../channel-handler.js'
4
4
  import { openChannel } from '../channel-runner.js'
5
5
  import type {
@@ -10,13 +10,13 @@ import type {
10
10
  } from '../channel.types.js'
11
11
  import { createHTTPInteraction } from '../../http/http-runner.js'
12
12
  import { ChannelStore } from '../channel-store.js'
13
- import { handleError } from '../../handle-error.js'
14
- import { PikkuUserSessionService } from '../../services/user-session-service.js'
15
- import { runMiddleware } from '../../middleware-runner.js'
16
- import { pikkuState } from '../../pikku-state.js'
13
+ import { handleHTTPError } from '../../../handle-error.js'
14
+ import { PikkuUserSessionService } from '../../../services/user-session-service.js'
15
+ import { runMiddleware } from '../../../middleware-runner.js'
16
+ import { pikkuState } from '../../../pikku-state.js'
17
17
  import { PikkuFetchHTTPRequest } from '../../http/pikku-fetch-http-request.js'
18
18
  import { PikkuHTTP } from '../../http/http.types.js'
19
- import { runPikkuFuncDirectly } from '../../function/function-runner.js'
19
+ import { runPikkuFuncDirectly } from '../../../function/function-runner.js'
20
20
 
21
21
  export interface RunServerlessChannelParams<ChannelData>
22
22
  extends RunChannelParams<ChannelData> {
@@ -124,7 +124,7 @@ export const runChannelConnect = async ({
124
124
  }
125
125
  http?.response?.status(101)
126
126
  } catch (e: any) {
127
- handleError(
127
+ handleHTTPError(
128
128
  e,
129
129
  http,
130
130
  channelId,
@@ -1,14 +1,14 @@
1
1
  import { test, describe, beforeEach, afterEach } from 'node:test'
2
2
  import * as assert from 'assert'
3
- import { NotFoundError } from '../errors/errors.js'
4
- import { JSONValue, PikkuMiddleware } from '../types/core.types.js'
3
+ import { NotFoundError } from '../../errors/errors.js'
4
+ import { JSONValue, PikkuMiddleware } from '../../types/core.types.js'
5
5
  import { fetch, addHTTPRoute } from './http-runner.js'
6
- import { pikkuState, resetPikkuState } from '../pikku-state.js'
6
+ import { pikkuState, resetPikkuState } from '../../pikku-state.js'
7
7
  import {
8
8
  PikkuMockRequest,
9
9
  PikkuMockResponse,
10
10
  } from '../channel/local/local-channel-runner.test.js'
11
- import { addFunction } from '../function/function-runner.js'
11
+ import { addFunction } from '../../function/function-runner.js'
12
12
 
13
13
  const sessionMiddleware: PikkuMiddleware = async (services, _, next) => {
14
14
  services.userSession.set({ userId: 'test' } as any)
@@ -12,25 +12,25 @@ import {
12
12
  CoreUserSession,
13
13
  PikkuMiddleware,
14
14
  SessionServices,
15
- } from '../types/core.types.js'
15
+ } from '../../types/core.types.js'
16
16
  import { match } from 'path-to-regexp'
17
- import { MissingSessionError, NotFoundError } from '../errors/errors.js'
17
+ import { MissingSessionError, NotFoundError } from '../../errors/errors.js'
18
18
  import {
19
19
  closeSessionServices,
20
20
  createWeakUID,
21
21
  isSerializable,
22
- } from '../utils.js'
22
+ } from '../../utils.js'
23
23
  import {
24
24
  PikkuUserSessionService,
25
25
  UserSessionService,
26
- } from '../services/user-session-service.js'
27
- import { runMiddleware } from '../middleware-runner.js'
28
- import { handleError } from '../handle-error.js'
29
- import { pikkuState } from '../pikku-state.js'
26
+ } from '../../services/user-session-service.js'
27
+ import { runMiddleware } from '../../middleware-runner.js'
28
+ import { handleHTTPError } from '../../handle-error.js'
29
+ import { pikkuState } from '../../pikku-state.js'
30
30
  import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js'
31
31
  import { PikkuFetchHTTPRequest } from './pikku-fetch-http-request.js'
32
32
  import { PikkuChannel } from '../channel/channel.types.js'
33
- import { addFunction, runPikkuFunc } from '../function/function-runner.js'
33
+ import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
34
34
  import { rpcService } from '../rpc/rpc-runner.js'
35
35
 
36
36
  /**
@@ -499,7 +499,7 @@ export const fetchData = async <In, Out>(
499
499
  return result
500
500
  } catch (e: any) {
501
501
  // Handle errors and, depending on configuration, bubble them up or respond with an error
502
- handleError(
502
+ handleHTTPError(
503
503
  e,
504
504
  http,
505
505
  requestId,
@@ -1,5 +1,5 @@
1
1
  import type { SerializeOptions } from 'cookie'
2
- import type { PikkuError } from '../errors/error-handler.js'
2
+ import type { PikkuError } from '../../errors/error-handler.js'
3
3
  import type {
4
4
  APIDocs,
5
5
  CoreServices,
@@ -7,13 +7,13 @@ import type {
7
7
  CoreUserSession,
8
8
  CreateSessionServices,
9
9
  PikkuMiddleware,
10
- } from '../types/core.types.js'
10
+ } from '../../types/core.types.js'
11
11
  import type {
12
12
  CoreAPIFunction,
13
13
  CoreAPIFunctionSessionless,
14
14
  CoreAPIPermission,
15
15
  CorePermissionGroup,
16
- } from '../function/functions.types.js'
16
+ } from '../../function/functions.types.js'
17
17
 
18
18
  type ExtractRouteParams<S extends string> =
19
19
  S extends `${string}:${infer Param}/${infer Rest}`
@@ -1,6 +1,5 @@
1
1
  export * from './pikku-fetch-http-request.js'
2
2
  export * from './pikku-fetch-http-response.js'
3
- export * from './incomingmessage-to-request-convertor.js'
4
3
  export * from './log-http-routes.js'
5
4
 
6
5
  export { fetch, fetchData, addHTTPRoute } from './http-runner.js'
@@ -1,5 +1,5 @@
1
- import { pikkuState } from '../pikku-state.js'
2
- import { Logger } from '../services/index.js'
1
+ import { pikkuState } from '../../pikku-state.js'
2
+ import { Logger } from '../../services/index.js'
3
3
 
4
4
  /**
5
5
  * Logs all the loaded routes.
@@ -1,7 +1,7 @@
1
1
  import { parse as parseQuery } from 'picoquery'
2
2
  import { parse as parseCookie } from 'cookie'
3
3
  import { HTTPMethod, PikkuHTTPRequest, PikkuQuery } from './http.types.js'
4
- import { UnprocessableContentError } from '../errors/errors.js'
4
+ import { UnprocessableContentError } from '../../errors/errors.js'
5
5
 
6
6
  /**
7
7
  * Abstract class representing a pikku request.
@@ -0,0 +1,2 @@
1
+ export * from './mcp.types.js'
2
+ export * from './mcp-runner.js'
@@ -0,0 +1,321 @@
1
+ import type {
2
+ CoreServices,
3
+ CoreSingletonServices,
4
+ CoreUserSession,
5
+ CreateSessionServices,
6
+ } from '../../types/core.types.js'
7
+ import type {
8
+ CoreMCPResource,
9
+ CoreMCPTool,
10
+ CoreMCPPrompt,
11
+ JsonRpcRequest,
12
+ JsonRpcResponse,
13
+ JsonRpcErrorResponse,
14
+ PikkuMCP,
15
+ } from './mcp.types.js'
16
+ import type { CoreAPIFunctionSessionless } from '../../function/functions.types.js'
17
+ import { getErrorResponse } from '../../errors/error-handler.js'
18
+ import { closeSessionServices } from '../../utils.js'
19
+ import { pikkuState } from '../../pikku-state.js'
20
+ import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
21
+ import { rpcService } from '../rpc/rpc-runner.js'
22
+ import { BadRequestError, NotFoundError } from '../../errors/errors.js'
23
+
24
+ export class MCPError extends Error {
25
+ constructor(public readonly error: JsonRpcErrorResponse) {
26
+ super(error?.message || 'MCP Error')
27
+ this.name = 'MCPError'
28
+ this.stack = new Error().stack
29
+ }
30
+ }
31
+
32
+ export type RunMCPEndpointParams<Tools extends string = any> = {
33
+ session?: CoreUserSession
34
+ singletonServices: CoreSingletonServices
35
+ mcp?: PikkuMCP<Tools>
36
+ createSessionServices?: CreateSessionServices<
37
+ CoreSingletonServices,
38
+ CoreServices<CoreSingletonServices>,
39
+ CoreUserSession
40
+ >
41
+ }
42
+
43
+ export type JsonRpcError = {
44
+ code: number
45
+ message: string
46
+ data?: any
47
+ }
48
+
49
+ export const addMCPResource = <
50
+ APIFunction extends CoreAPIFunctionSessionless<any, any>,
51
+ >(
52
+ mcpResource: CoreMCPResource<APIFunction>
53
+ ) => {
54
+ const resourcesMeta = pikkuState('mcp', 'resourcesMeta')
55
+ const mcpResourceMeta = resourcesMeta[mcpResource.uri]
56
+ if (!mcpResourceMeta) {
57
+ throw new Error(`MCP resource metadata not found for '${mcpResource.uri}'`)
58
+ }
59
+ addFunction(mcpResourceMeta.pikkuFuncName, {
60
+ func: mcpResource.func,
61
+ })
62
+ const resources = pikkuState('mcp', 'resources')
63
+ if (resources.has(mcpResource.uri)) {
64
+ throw new Error(`MCP resource already exists: ${mcpResource.uri}`)
65
+ }
66
+ resources.set(mcpResource.uri, mcpResource)
67
+ }
68
+
69
+ export const addMCPTool = <
70
+ APIFunction extends CoreAPIFunctionSessionless<any, any>,
71
+ >(
72
+ mcpTool: CoreMCPTool<APIFunction>
73
+ ) => {
74
+ const toolsMeta = pikkuState('mcp', 'toolsMeta')
75
+ const mcpToolMeta = toolsMeta[mcpTool.name]
76
+ if (!mcpToolMeta) {
77
+ throw new Error(`MCP tool metadata not found for '${mcpTool.name}'`)
78
+ }
79
+ addFunction(mcpToolMeta.pikkuFuncName, {
80
+ func: mcpTool.func,
81
+ })
82
+ const tools = pikkuState('mcp', 'tools')
83
+ if (tools.has(mcpTool.name)) {
84
+ throw new Error(`MCP tool already exists: ${mcpTool.name}`)
85
+ }
86
+ tools.set(mcpTool.name, mcpTool)
87
+ }
88
+
89
+ export const addMCPPrompt = <
90
+ APIFunction extends CoreAPIFunctionSessionless<any, any>,
91
+ >(
92
+ mcpPrompt: CoreMCPPrompt<APIFunction>
93
+ ) => {
94
+ const promptsMeta = pikkuState('mcp', 'promptsMeta')
95
+ const mcpPromptMeta = promptsMeta[mcpPrompt.name]
96
+ if (!mcpPromptMeta) {
97
+ throw new Error(`MCP prompt metadata not found for '${mcpPrompt.name}'`)
98
+ }
99
+ addFunction(mcpPromptMeta.pikkuFuncName, {
100
+ func: mcpPrompt.func,
101
+ })
102
+ const prompts = pikkuState('mcp', 'prompts')
103
+ if (prompts.has(mcpPrompt.name)) {
104
+ throw new Error(`MCP prompt already exists: ${mcpPrompt.name}`)
105
+ }
106
+ prompts.set(mcpPrompt.name, mcpPrompt)
107
+ }
108
+
109
+ export async function runMCPResource(
110
+ request: JsonRpcRequest,
111
+ params: RunMCPEndpointParams,
112
+ uri: string
113
+ ) {
114
+ let endpoint: CoreMCPResource | undefined
115
+ let pikkuFuncName: string | undefined
116
+ let extractedParams: Record<string, string> = {}
117
+
118
+ const metas = pikkuState('mcp', 'resourcesMeta')
119
+ const endpoints = pikkuState('mcp', 'resources')
120
+
121
+ if (endpoints.has(uri)) {
122
+ endpoint = endpoints.get(uri)
123
+ pikkuFuncName = metas[uri]?.pikkuFuncName
124
+ } else {
125
+ for (const [uriTemplate, value] of endpoints.entries()) {
126
+ // Extract parameter names from the template
127
+ const paramNames = Array.from(
128
+ uriTemplate.matchAll(/\{([^}]+)\}/g),
129
+ (m) => m[1]
130
+ )
131
+
132
+ // Create regex pattern to match and capture parameter values
133
+ const regexPattern = uriTemplate.replace(/\{[^}]+\}/g, '([^/]+)')
134
+ const regex = new RegExp(`^${regexPattern}$`)
135
+ const match = uri.match(regex)
136
+
137
+ if (match) {
138
+ endpoint = value
139
+ pikkuFuncName = metas[uriTemplate]?.pikkuFuncName
140
+
141
+ // Extract parameter values and create params object
142
+ for (let i = 0; i < paramNames.length; i++) {
143
+ extractedParams[paramNames[i]!] = match[i + 1]! // match[0] is the full match
144
+ }
145
+ break
146
+ }
147
+ }
148
+ }
149
+
150
+ return await runMCPPikkuFunc(
151
+ {
152
+ ...request,
153
+ params: { ...request.params, ...extractedParams },
154
+ },
155
+ 'resource',
156
+ uri,
157
+ endpoint,
158
+ pikkuFuncName,
159
+ { ...params, mcp: { uri } } as RunMCPEndpointParams<keyof CoreMCPResource>
160
+ )
161
+ }
162
+
163
+ export async function runMCPTool(
164
+ request: JsonRpcRequest,
165
+ params: RunMCPEndpointParams,
166
+ name: string
167
+ ) {
168
+ const endpoint = pikkuState('mcp', 'tools').get(name)
169
+ const meta = pikkuState('mcp', 'toolsMeta')[name]
170
+ return await runMCPPikkuFunc(
171
+ request,
172
+ 'tool',
173
+ name,
174
+ endpoint,
175
+ meta?.pikkuFuncName,
176
+ params
177
+ )
178
+ }
179
+
180
+ export async function runMCPPrompt(
181
+ request: JsonRpcRequest,
182
+ params: RunMCPEndpointParams,
183
+ name: string
184
+ ) {
185
+ const endpoint = pikkuState('mcp', 'prompts').get(name)
186
+ const meta = pikkuState('mcp', 'promptsMeta')[name]
187
+ return await runMCPPikkuFunc(
188
+ request,
189
+ 'prompt',
190
+ name,
191
+ endpoint,
192
+ meta?.pikkuFuncName,
193
+ params
194
+ )
195
+ }
196
+
197
+ /**
198
+ * JSON-RPC 2.0 compatible MCP endpoint runner
199
+ */
200
+ async function runMCPPikkuFunc(
201
+ request: JsonRpcRequest,
202
+ type: 'resource' | 'tool' | 'prompt',
203
+ name: string,
204
+ mcp: CoreMCPResource | CoreMCPTool | CoreMCPPrompt | undefined,
205
+ pikkuFuncName: string | undefined,
206
+ {
207
+ session,
208
+ singletonServices,
209
+ createSessionServices,
210
+ mcp: interaction,
211
+ }: RunMCPEndpointParams
212
+ ): Promise<JsonRpcResponse> {
213
+ let sessionServices: any
214
+
215
+ try {
216
+ // Validate JSON-RPC request structure
217
+ if (request.jsonrpc !== '2.0') {
218
+ throw new BadRequestError(
219
+ 'Invalid JSON-RPC version, only supoorted version is 2.0'
220
+ )
221
+ }
222
+
223
+ if (!mcp) {
224
+ throw new NotFoundError(
225
+ `MCP '${type}' registration not found for '${name}'`
226
+ )
227
+ }
228
+
229
+ if (!pikkuFuncName) {
230
+ throw new NotFoundError(
231
+ `MCP '${type}' PikkuFunction Mapping not found for '${name}'`
232
+ )
233
+ }
234
+
235
+ singletonServices.logger.debug(`Running MCP ${type}: ${name}`)
236
+
237
+ const getAllServices = async () => {
238
+ if (createSessionServices) {
239
+ const services = await createSessionServices(
240
+ singletonServices,
241
+ { mcp: interaction },
242
+ session
243
+ )
244
+ sessionServices = services
245
+ return rpcService.injectRPCService({
246
+ ...singletonServices,
247
+ ...services,
248
+ mcp: interaction,
249
+ })
250
+ }
251
+ return rpcService.injectRPCService({
252
+ ...singletonServices,
253
+ mcp: interaction,
254
+ })
255
+ }
256
+
257
+ const result = await runPikkuFunc(pikkuFuncName, {
258
+ getAllServices,
259
+ session,
260
+ data: request.params,
261
+ })
262
+
263
+ return {
264
+ id: request.id,
265
+ result,
266
+ }
267
+ } catch (e: any) {
268
+ singletonServices.logger.error(
269
+ `Error running MCP ${type} '${name}':`,
270
+ e.constructor
271
+ )
272
+ const errorResponse = getErrorResponse(e)
273
+ if (errorResponse?.mcpCode) {
274
+ throw new MCPError({
275
+ id: request.id,
276
+ code: errorResponse.mcpCode,
277
+ message: errorResponse.message,
278
+ })
279
+ } else {
280
+ if (errorResponse) {
281
+ singletonServices.logger.warn(
282
+ `Got error without a mapping: ${errorResponse.message}`
283
+ )
284
+ }
285
+ throw new MCPError({
286
+ id: request.id,
287
+ code: -32603,
288
+ message: 'Internal error',
289
+ data: { message: e.message, stack: e.stack },
290
+ })
291
+ }
292
+ } finally {
293
+ if (sessionServices) {
294
+ await closeSessionServices(singletonServices.logger, sessionServices)
295
+ }
296
+ }
297
+ }
298
+
299
+ export const getMCPTools = () => {
300
+ return pikkuState('mcp', 'tools')
301
+ }
302
+
303
+ export const getMCPResources = () => {
304
+ return pikkuState('mcp', 'resources')
305
+ }
306
+
307
+ export const getMCPResourcesMeta = () => {
308
+ return pikkuState('mcp', 'resourcesMeta')
309
+ }
310
+
311
+ export const getMCPToolsMeta = () => {
312
+ return pikkuState('mcp', 'toolsMeta')
313
+ }
314
+
315
+ export const getMCPPrompts = () => {
316
+ return pikkuState('mcp', 'prompts')
317
+ }
318
+
319
+ export const getMCPPromptsMeta = () => {
320
+ return pikkuState('mcp', 'promptsMeta')
321
+ }