@jissp/nestjs-mcp-server 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # nestjs-mcp-server
2
2
 
3
- > **참고**: 이 프로젝트는 토이프로젝트 또는 개인적인 용도로 로컬 MCP 구현하기 위해 만든 Simple 모듈입니다.
4
- >
5
- NestJS 기반의 Model Context Protocol (MCP) 서버 라이브러리입니다.
3
+ > **참고**: 이 프로젝트는 NestJS 환경에서 Model Context Protocol (MCP) 서버를 간편하게 구축하기 위한 라이브러리입니다.
6
4
 
7
- 데코레이터와 메타데이터 스캐닝을 통해 MCP 리소스와 도구를 간편하게 정의하고 관리할 수 있습니다.
5
+ NestJS의 강력한 데코레이터와 메타데이터 스캐닝 기능을 활용하여 MCP 리소스(Resource)와 도구(Tool)를 정의하고 관리할 수 있습니다.
6
+
7
+ 최신 버전은 공식 `@modelcontextprotocol/sdk`를 기반으로 하며, **SSE(Server-Sent Events)** 전송 방식을 지원합니다.
8
8
 
9
9
  ## 설치
10
10
 
@@ -14,17 +14,17 @@ npm install @jissp/nestjs-mcp-server
14
14
 
15
15
  ## 주요 기능
16
16
 
17
- - **데코레이터 기반 정의**: `@McpTool`, `@McpResource`, `@McpSchemaProperty` 데코레이터로 리소스와 도구 정의
18
- - **자동 메타데이터 스캔**: 클래스와 메서드의 메타데이터 자동 수집
19
- - **Executor 패턴**: 커스텀 executor를 통한 확장 가능한 아키텍처
20
- - **JSON-RPC 통신**: 표준 JSON-RPC 2.0 프로토콜 지원
21
- - **세션 관리**: 세션 ID 기반 요청 추적 (Header: `mcp-session-id`)
17
+ - **공식 MCP SDK 기반**: `@modelcontextprotocol/sdk`를 사용하여 표준 규격 완벽 준수
18
+ - **SSE 전송 방식**: 실시간 양방향 통신을 위한 SSE(Server-Sent Events) 지원
19
+ - **데코레이터 기반 정의**: `@McpTool`, `@McpResource`, `@McpSchemaProperty`를 이용한 간결한 정의
20
+ - **자동 메타데이터 스캔**: 클래스와 메서드에 정의된 MCP 메타데이터 자동 수집 및 등록
21
+ - **Executor 패턴**: 비즈니스 로직을 분리하여 확장 가능한 구조 제공
22
22
 
23
23
  ## 빠른 시작
24
24
 
25
25
  ### 1. 전역 모듈 설정
26
26
 
27
- `AppModule`에서 `forRoot()`를 호출하여 MCP 서버의 핵심 인프라(Controller, Service 등)를 전역적으로 설정합니다.
27
+ `AppModule`에서 `McpServerModule.forRoot()`를 호출하여 MCP 서버 인프라를 설정합니다.
28
28
 
29
29
  ```typescript
30
30
  import { Module } from '@nestjs/common';
@@ -35,20 +35,14 @@ import { McpServerModule } from '@jissp/nestjs-mcp-server';
35
35
  McpServerModule.forRoot(),
36
36
  ],
37
37
  })
38
- export class AppModule {
39
- }
38
+ export class AppModule {}
40
39
  ```
41
40
 
42
41
  ### 2. 도구(Tool) 및 리소스(Resource) 등록
43
42
 
44
- `forFeature()`를 사용하여 특정 도구나 리소스를 구현한 `executors`를 등록합니다.
43
+ `forFeature()`를 사용하여 MCP 요소들을 구현한 프로바이더를 등록합니다.
45
44
 
46
45
  ```typescript
47
- import { Module } from '@nestjs/common';
48
- import { McpServerModule } from '@jissp/nestjs-mcp-server';
49
- import { MyToolExecutor } from './my-tool.executor';
50
- import { MyResourceService } from './my-resource.service';
51
-
52
46
  @Module({
53
47
  imports: [
54
48
  McpServerModule.forFeature({
@@ -56,25 +50,44 @@ import { MyResourceService } from './my-resource.service';
56
50
  }),
57
51
  ],
58
52
  })
59
- export class MyFeatureModule {
53
+ export class MyFeatureModule {}
54
+ ```
55
+
56
+ ### 3. 클라이언트 설정 (예: Claude Desktop)
57
+
58
+ 클라이언트(예: Claude Desktop)에서 이 서버를 사용하려면 다음과 같이 SSE 방식으로 설정해야 합니다.
59
+
60
+ **`config.json` 예시:**
61
+
62
+ ```json
63
+ {
64
+ "mcpServers": {
65
+ "my-nestjs-server": {
66
+ "command": "node",
67
+ "args": ["/path/to/your/server/dist/main.js"],
68
+ "env": {
69
+ "MCP_TRANSPORT": "sse",
70
+ "MCP_ENDPOINT": "http://localhost:3000/mcp/sse"
71
+ }
72
+ }
73
+ }
60
74
  }
61
75
  ```
62
76
 
63
- ### 3. 도구(Tool) 정의
77
+ *참고: 서버 주소는 기본적으로 `http://localhost:3000/mcp/sse`가 엔드포인트가 됩니다.*
64
78
 
65
- 도구는 클래스 메서드에 `@McpTool` 데코레이터를 사용하여 정의합니다. 입력 스키마는 직접 JSON Schema 객체로 전달하거나, `@McpSchemaProperty`가 적용된 클래스를 전달하여 자동으로 생성할 수 있습니다.
79
+ ## 정의 가이드
66
80
 
67
- #### JSON Schema 직접 정의 방식
81
+ ### 도구(Tool) 정의
68
82
 
69
- ```typescript
70
- import { Injectable } from '@nestjs/common';
71
- import { McpTool, JsonRpcCallRequest } from '@jissp/nestjs-mcp-server';
83
+ `BaseExecutor` 인터페이스를 구현하고 `@McpTool` 데코레이터를 사용합니다.
72
84
 
85
+ ```typescript
73
86
  @Injectable()
74
- export class MyToolExecutor {
87
+ export class CalculateSumExecutor implements BaseExecutor {
75
88
  @McpTool({
76
89
  name: 'calculate-sum',
77
- description: '두 숫자의 합을 계산합니다.',
90
+ description: '두 숫자의 합을 구합니다.',
78
91
  inputSchema: {
79
92
  type: 'object',
80
93
  properties: {
@@ -84,133 +97,37 @@ export class MyToolExecutor {
84
97
  required: ['a', 'b'],
85
98
  },
86
99
  })
87
- async execute(request: JsonRpcCallRequest) {
88
- const { a, b } = request.params.arguments as { a: number; b: number };
89
- return a + b;
90
- }
91
- }
92
- ```
93
-
94
- #### @McpSchemaProperty 클래스 기반 방식
95
-
96
- ```typescript
97
- import { Injectable } from '@nestjs/common';
98
- import { McpTool, McpSchemaProperty } from '@jissp/nestjs-mcp-server';
99
-
100
- export class CalculateSumDto {
101
- @McpSchemaProperty({
102
- type: 'number',
103
- description: '첫 번째 숫자',
104
- isRequired: true,
105
- })
106
- a: number;
107
-
108
- @McpSchemaProperty({
109
- type: 'number',
110
- description: '두 번째 숫자',
111
- isRequired: true,
112
- })
113
- b: number;
114
- }
115
-
116
- @Injectable()
117
- export class MyToolExecutor {
118
- @McpTool({
119
- name: 'calculate-sum',
120
- description: '두 숫자의 합을 계산합니다.',
121
- inputSchema: CalculateSumDto,
122
- })
123
- async execute(params: CalculateSumDto) {
124
- return params.a + params.b;
100
+ async execute(request: JsonRpcToolRequest) {
101
+ const { a, b } = request.params.arguments;
102
+ return { result: a + b };
125
103
  }
126
104
  }
127
105
  ```
128
106
 
129
- ### 4. 리소스(Resource) 정의
107
+ ### 리소스(Resource) 정의
130
108
 
131
- 리소스는 서비스 내의 메서드에 `@McpResource` 데코레이터를 사용하여 정의합니다. URI 템플릿을 지원합니다.
109
+ 메서드에 `@McpResource` 데코레이터를 사용하며, URI 템플릿(`{param}`)을 지원합니다.
132
110
 
133
111
  ```typescript
134
- import { Injectable } from '@nestjs/common';
135
- import { McpResource } from '@jissp/nestjs-mcp-server';
136
-
137
112
  @Injectable()
138
113
  export class MyResourceService {
139
114
  @McpResource({
140
- uri: 'file://{path}',
141
- name: 'system-file',
142
- description: '시스템 파일을 읽어옵니다.',
143
- mimeType: 'text/plain',
115
+ uri: 'docs://{topic}',
116
+ name: 'documentation',
117
+ description: '주제별 문서를 제공합니다.',
118
+ mimeType: 'text/markdown',
144
119
  })
145
- async getFileContent(request: any) {
146
- const { path } = request.params.arguments;
147
- // 파일 읽기 로직...
148
- return `Content of ${path}`;
120
+ async getDoc(request: any) {
121
+ const { topic } = request.params.arguments;
122
+ return `Content for ${topic}...`;
149
123
  }
150
124
  }
151
125
  ```
152
126
 
153
- ## API 레퍼런스
154
-
155
- ### McpServerModule
156
-
157
- #### `forRoot()`
158
-
159
- MCP 서버의 핵심 기능(SSE 스트림, JSON-RPC 처리 등)을 전역 모듈로 등록합니다.
160
-
161
- #### `forFeature(options: McpServerFeatureOptions)`
162
-
163
- 특정 도구(Tool)나 리소스(Resource)를 담당할 프로바이더를 등록합니다.
164
-
165
- - `executors`: `BaseExecutor`를 구현하거나 MCP 데코레이터가 사용된 프로바이더 배열.
166
- - `imports`: (선택) `executors`가 의존하는 다른 모듈들.
167
-
168
- ### 데코레이터
169
-
170
- #### `@McpTool(options: McpToolOptions)`
171
-
172
- 메서드를 MCP 도구로 등록합니다.
173
-
174
- ```typescript
175
- {
176
- name: string; // 도구 이름 (필수)
177
- description ? : string; // 도구 설명
178
- inputSchema ? : any; // JSON Schema 형식의 입력 파라미터 정의 또는 DTO 클래스
179
- }
180
- ```
181
-
182
- #### `@McpSchemaProperty(options: McpSchemaPropertyOptions)`
183
-
184
- 입력 파라미터 DTO의 속성을 정의하여 자동으로 JSON Schema를 생성합니다.
185
-
186
- ```typescript
187
- {
188
- type: string; // 데이터 타입 (예: 'string', 'number', 'boolean')
189
- description: string; // 속성 설명
190
- isRequired: boolean; // 필수 여부
191
- }
192
- ```
193
-
194
- #### `@McpResource(options: McpResourceOptions)`
195
-
196
- 메서드를 MCP 리소스로 등록합니다.
197
-
198
- ```typescript
199
- {
200
- uri: string; // 리소스 URI 템플릿 (필수, 예: 'myschema://{id}')
201
- name: string; // 리소스 이름 (필수)
202
- description ? : string; // 리소스 설명
203
- mimeType ? : string; // 리소스의 MIME 타입
204
- }
205
- ```
206
-
207
- ## 아키텍처
208
-
209
- ### 요청 흐름
127
+ ## 아키텍처 및 요청 흐름
210
128
 
211
- 1. **클라이언트 SSE 연결** `GET /mcp` (Header에 `mcp-session-id` 포함 권장)
212
- 2. **JSON-RPC 요청** `POST /mcp`
213
- 3. **McpServerController** 요청 수신 세션 검증 (`McpSessionIdGuard`)
214
- 4. **McpServerService** 메서드 핸들러 조회 `McpMetadataRegistryService` 참조
215
- 5. **Executor / Handler** `@McpTool` 클래스의 `execute` 또는 `@McpResource` 메서드 실행
216
- 6. **응답** → JSON-RPC 결과 반환
129
+ 1. **SSE 연결**: 클라이언트가 `GET /mcp/sse`에 접속하여 서버와 연결을 수립합니다.
130
+ 2. **엔드포인트 통지**: 서버는 연결 성공 시 클라이언트에게 메시지를 보낼 POST 엔드포인트(` /mcp/message?sessionId=...`)를 알립니다.
131
+ 3. **메시지 전송**: 클라이언트가 도구 실행 등을 요청할 해당 POST 엔드포인트로 JSON-RPC 메시지를 보냅니다.
132
+ 4. **핸들러 실행**: `McpServerService`가 SDK를 통해 요청을 해석하고, `McpMetadataRegistryService`에 등록된 핸들러를 찾아 실행합니다.
133
+ 5. **응답 반환**: 실행 결과가 SSE 스트림을 통해 다시 클라이언트에게 전달됩니다.
@@ -1,7 +1,12 @@
1
+ import { ConstructorType } from '@jissp/metadata-scanner';
2
+ import { InputSchemaProperty } from './mcp-tool.decorator';
1
3
  export declare const MCP_SCHEMA_PROPERTIES_METADATA = "MCP_SCHEMA_PROPERTIES_METADATA";
2
4
  export interface McpSchemaPropertyOptions {
3
5
  type: string;
4
6
  description: string;
5
7
  isRequired: boolean;
8
+ enum?: (string | number)[];
9
+ items?: ConstructorType | InputSchemaProperty;
10
+ properties?: ConstructorType;
6
11
  }
7
12
  export declare const McpSchemaProperty: (options?: McpSchemaPropertyOptions | undefined) => (...args: [target: Object, propertyKey: string | symbol]) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-schema-property.decorator.js","sourceRoot":"","sources":["../../lib/mcp-server/decorators/mcp-schema-property.decorator.ts"],"names":[],"mappings":";;;AAAA,8DAAkE;AAErD,QAAA,8BAA8B,GAAG,gCAAgC,CAAC;AAQlE,QAAA,iBAAiB,GAC5B,IAAA,0CAAuB,EACrB,sCAA8B,CAC/B,CAAC"}
1
+ {"version":3,"file":"mcp-schema-property.decorator.js","sourceRoot":"","sources":["../../lib/mcp-server/decorators/mcp-schema-property.decorator.ts"],"names":[],"mappings":";;;AAAA,8DAGiC;AAGpB,QAAA,8BAA8B,GAAG,gCAAgC,CAAC;AAmBlE,QAAA,iBAAiB,GAC5B,IAAA,0CAAuB,EACrB,sCAA8B,CAC/B,CAAC"}
@@ -12,6 +12,10 @@ export interface JsonSchema {
12
12
  export interface InputSchemaProperty {
13
13
  type: string;
14
14
  description?: string;
15
+ enum?: (string | number)[];
16
+ items?: InputSchemaProperty;
17
+ properties?: InputSchemaProperties;
18
+ required?: string[];
15
19
  }
16
20
  export type InputSchemaProperties = Record<string, InputSchemaProperty>;
17
21
  export declare const McpTool: import("@nestjs/core").ReflectableDecorator<McpToolOptions, McpToolOptions>;
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-tool.decorator.js","sourceRoot":"","sources":["../../lib/mcp-server/decorators/mcp-tool.decorator.ts"],"names":[],"mappings":";;;AAAA,uCAAyC;AAsB5B,QAAA,OAAO,GAAG,gBAAS,CAAC,eAAe,EAAkB,CAAC"}
1
+ {"version":3,"file":"mcp-tool.decorator.js","sourceRoot":"","sources":["../../lib/mcp-server/decorators/mcp-tool.decorator.ts"],"names":[],"mappings":";;;AAAA,uCAAyC;AA0B5B,QAAA,OAAO,GAAG,gBAAS,CAAC,eAAe,EAAkB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -7,3 +7,4 @@ export * from './mcp-metadata-input-schema.builder';
7
7
  export * from './mcp-server.controller';
8
8
  export * from './mcp-server.service';
9
9
  export * from './mcp-server.module';
10
+ export * from './transports/sse-http-transport';
package/dist/index.js CHANGED
@@ -23,4 +23,5 @@ __exportStar(require("./mcp-metadata-input-schema.builder"), exports);
23
23
  __exportStar(require("./mcp-server.controller"), exports);
24
24
  __exportStar(require("./mcp-server.service"), exports);
25
25
  __exportStar(require("./mcp-server.module"), exports);
26
+ __exportStar(require("./transports/sse-http-transport"), exports);
26
27
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/mcp-server/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,+CAA6B;AAC7B,qDAAmC;AACnC,kDAAgC;AAChC,kEAAgD;AAChD,sEAAoD;AACpD,0DAAwC;AACxC,uDAAqC;AACrC,sDAAoC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/mcp-server/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,+CAA6B;AAC7B,qDAAmC;AACnC,kDAAgC;AAChC,kEAAgD;AAChD,sEAAoD;AACpD,0DAAwC;AACxC,uDAAqC;AACrC,sDAAoC;AACpC,kEAAgD"}
@@ -6,5 +6,6 @@ export declare class McpMetadataInputSchemaBuilder {
6
6
  build(mcpToolOptions: McpToolOptions): JsonSchema | undefined;
7
7
  private generateInputSchema;
8
8
  private buildSchemaProperties;
9
+ private toInputSchemaProperty;
9
10
  private buildRequired;
10
11
  }
@@ -35,13 +35,33 @@ let McpMetadataInputSchemaBuilder = class McpMetadataInputSchemaBuilder {
35
35
  buildSchemaProperties(propertiesMetadata) {
36
36
  const entries = propertiesMetadata.map(({ key, metadata }) => [
37
37
  key,
38
- {
39
- type: metadata.type,
40
- description: metadata.description,
41
- },
38
+ this.toInputSchemaProperty(metadata),
42
39
  ]);
43
40
  return Object.fromEntries(entries);
44
41
  }
42
+ toInputSchemaProperty(options) {
43
+ const property = {
44
+ type: options.type,
45
+ description: options.description,
46
+ };
47
+ if (options.enum) {
48
+ property.enum = options.enum;
49
+ }
50
+ if (options.items !== undefined) {
51
+ property.items = (0, metadata_scanner_1.isConstructorType)(options.items)
52
+ ? this.generateInputSchema(options.items)
53
+ : options.items;
54
+ }
55
+ if (options.properties !== undefined &&
56
+ (0, metadata_scanner_1.isConstructorType)(options.properties)) {
57
+ const nested = this.generateInputSchema(options.properties);
58
+ property.properties = nested.properties;
59
+ if (nested.required && nested.required.length > 0) {
60
+ property.required = nested.required;
61
+ }
62
+ }
63
+ return property;
64
+ }
45
65
  buildRequired(propertiesMetadata) {
46
66
  const filteredEntries = propertiesMetadata.filter(({ metadata: { isRequired } }) => {
47
67
  return isRequired;
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-metadata-input-schema.builder.js","sourceRoot":"","sources":["../lib/mcp-server/mcp-metadata-input-schema.builder.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,8DAKiC;AACjC,6CAOsB;AAGf,IAAM,6BAA6B,GAAnC,MAAM,6BAA6B;IAErB;IADnB,YACmB,sBAA8C;QAA9C,2BAAsB,GAAtB,sBAAsB,CAAwB;IAC9D,CAAC;IAEG,KAAK,CAAC,cAA8B;QACzC,IAAI,CAAC,IAAA,oCAAiB,EAAC,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;YACnD,OAAO,cAAc,CAAC,WAAW,CAAC;QACpC,CAAC;QAED,OAAO,IAAI,CAAC,mBAAmB,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IAC9D,CAAC;IAEO,mBAAmB,CAAC,gBAAiC;QAC3D,MAAM,UAAU,GACd,IAAI,CAAC,sBAAsB,CAAC,cAAc,CACxC,2CAA8B,EAC9B,gBAAgB,CACjB,CAAC;QAEJ,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC;YAClD,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC;SACzC,CAAC;IACJ,CAAC;IAEO,qBAAqB,CAC3B,kBAAuE;QAEvE,MAAM,OAAO,GAAG,kBAAkB,CAAC,GAAG,CACpC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAiC,EAAE,CAAC;YACpD,GAAG;YACH;gBACE,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,WAAW,EAAE,QAAQ,CAAC,WAAW;aAClC;SACF,CACF,CAAC;QAEF,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAEO,aAAa,CACnB,kBAAuE;QAEvE,MAAM,eAAe,GAAG,kBAAkB,CAAC,MAAM,CAC/C,CAAC,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE;YAC/B,OAAO,UAAU,CAAC;QACpB,CAAC,CACF,CAAC;QAEF,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;CACF,CAAA;AAtDY,sEAA6B;wCAA7B,6BAA6B;IADzC,IAAA,mBAAU,GAAE;qCAGgC,yCAAsB;GAFtD,6BAA6B,CAsDzC"}
1
+ {"version":3,"file":"mcp-metadata-input-schema.builder.js","sourceRoot":"","sources":["../lib/mcp-server/mcp-metadata-input-schema.builder.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,8DAKiC;AACjC,6CAOsB;AAGf,IAAM,6BAA6B,GAAnC,MAAM,6BAA6B;IAErB;IADnB,YACmB,sBAA8C;QAA9C,2BAAsB,GAAtB,sBAAsB,CAAwB;IAC9D,CAAC;IAEG,KAAK,CAAC,cAA8B;QACzC,IAAI,CAAC,IAAA,oCAAiB,EAAC,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;YACnD,OAAO,cAAc,CAAC,WAAW,CAAC;QACpC,CAAC;QAED,OAAO,IAAI,CAAC,mBAAmB,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IAC9D,CAAC;IAEO,mBAAmB,CAAC,gBAAiC;QAC3D,MAAM,UAAU,GACd,IAAI,CAAC,sBAAsB,CAAC,cAAc,CACxC,2CAA8B,EAC9B,gBAAgB,CACjB,CAAC;QAEJ,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC;YAClD,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC;SACzC,CAAC;IACJ,CAAC;IAEO,qBAAqB,CAC3B,kBAAuE;QAEvE,MAAM,OAAO,GAAG,kBAAkB,CAAC,GAAG,CACpC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAiC,EAAE,CAAC;YACpD,GAAG;YACH,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC;SACrC,CACF,CAAC;QAEF,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAEO,qBAAqB,CAC3B,OAAiC;QAEjC,MAAM,QAAQ,GAAwB;YACpC,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,WAAW,EAAE,OAAO,CAAC,WAAW;SACjC,CAAC;QAEF,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,QAAQ,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAC/B,CAAC;QAED,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAChC,QAAQ,CAAC,KAAK,GAAG,IAAA,oCAAiB,EAAC,OAAO,CAAC,KAAK,CAAC;gBAC/C,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,KAAK,CAAC;gBACzC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;QACpB,CAAC;QAED,IACE,OAAO,CAAC,UAAU,KAAK,SAAS;YAChC,IAAA,oCAAiB,EAAC,OAAO,CAAC,UAAU,CAAC,EACrC,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC5D,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;YACxC,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAClD,QAAQ,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;YACtC,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,aAAa,CACnB,kBAAuE;QAEvE,MAAM,eAAe,GAAG,kBAAkB,CAAC,MAAM,CAC/C,CAAC,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE;YAC/B,OAAO,UAAU,CAAC;QACpB,CAAC,CACF,CAAC;QAEF,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;CACF,CAAA;AAnFY,sEAA6B;wCAA7B,6BAA6B;IADzC,IAAA,mBAAU,GAAE;qCAGgC,yCAAsB;GAFtD,6BAA6B,CAmFzC"}
@@ -1,12 +1,9 @@
1
- import { MessageEvent } from '@nestjs/common';
2
1
  import type { Request, Response } from 'express';
3
- import { Observable } from 'rxjs';
4
- import type { JsonRpcRequest } from './mcp-server.types';
5
2
  import { McpServerService } from './mcp-server.service';
6
3
  export declare class McpServerController {
7
4
  private readonly mcpService;
8
- private eventSubject;
5
+ private readonly transportMap;
9
6
  constructor(mcpService: McpServerService);
10
- sse(req: Request): Observable<MessageEvent>;
11
- handleIncomingMessage(body: JsonRpcRequest, res: Response): Promise<Response<any, Record<string, any>>>;
7
+ sse(req: Request, res: Response): Promise<void>;
8
+ handleMessage(req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
12
9
  }
@@ -14,49 +14,53 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.McpServerController = void 0;
16
16
  const common_1 = require("@nestjs/common");
17
- const rxjs_1 = require("rxjs");
18
- const guards_1 = require("./guards");
19
17
  const mcp_server_service_1 = require("./mcp-server.service");
18
+ const sse_http_transport_1 = require("./transports/sse-http-transport");
20
19
  let McpServerController = class McpServerController {
21
20
  mcpService;
22
- eventSubject = new rxjs_1.Subject();
21
+ transportMap = new Map();
23
22
  constructor(mcpService) {
24
23
  this.mcpService = mcpService;
25
24
  }
26
- sse(req) {
27
- const sessionId = req.headers['mcp-session-id'];
28
- console.log(`SSE Connected with Session ID: ${sessionId?.toString() ?? 'unknown'}`);
29
- return this.eventSubject.asObservable();
25
+ async sse(req, res) {
26
+ const transport = new sse_http_transport_1.SseHttpTransport('/mcp/message');
27
+ const server = this.mcpService.createConfiguredServer();
28
+ transport.onclose = () => {
29
+ this.transportMap.delete(transport.sessionId);
30
+ };
31
+ transport.attachResponse(res);
32
+ await server.connect(transport);
33
+ this.transportMap.set(transport.sessionId, transport);
34
+ req.on('close', () => transport.close());
30
35
  }
31
- async handleIncomingMessage(body, res) {
32
- const response = await this.mcpService.handleMessage(body);
33
- if (!response) {
34
- return res.status(common_1.HttpStatus.ACCEPTED).send();
36
+ async handleMessage(req, res) {
37
+ const sessionId = req.query['sessionId'];
38
+ const transport = this.transportMap.get(sessionId);
39
+ if (!transport) {
40
+ return res
41
+ .status(common_1.HttpStatus.NOT_FOUND)
42
+ .json({ error: 'Session not found' });
35
43
  }
36
- if (body.method === 'initialize') {
37
- res.setHeader('MCP-Session-Id', crypto.randomUUID());
38
- }
39
- return res.status(common_1.HttpStatus.OK).json(response.result);
44
+ await transport.handlePostMessage(req, res);
40
45
  }
41
46
  };
42
47
  exports.McpServerController = McpServerController;
43
48
  __decorate([
44
- (0, common_1.Sse)(),
45
- (0, common_1.Get)(),
49
+ (0, common_1.Get)('sse'),
46
50
  __param(0, (0, common_1.Req)()),
51
+ __param(1, (0, common_1.Res)()),
47
52
  __metadata("design:type", Function),
48
- __metadata("design:paramtypes", [Object]),
49
- __metadata("design:returntype", rxjs_1.Observable)
53
+ __metadata("design:paramtypes", [Object, Object]),
54
+ __metadata("design:returntype", Promise)
50
55
  ], McpServerController.prototype, "sse", null);
51
56
  __decorate([
52
- (0, common_1.UseGuards)(guards_1.McpSessionIdGuard),
53
- (0, common_1.Post)(),
54
- __param(0, (0, common_1.Body)()),
57
+ (0, common_1.Post)('message'),
58
+ __param(0, (0, common_1.Req)()),
55
59
  __param(1, (0, common_1.Res)()),
56
60
  __metadata("design:type", Function),
57
61
  __metadata("design:paramtypes", [Object, Object]),
58
62
  __metadata("design:returntype", Promise)
59
- ], McpServerController.prototype, "handleIncomingMessage", null);
63
+ ], McpServerController.prototype, "handleMessage", null);
60
64
  exports.McpServerController = McpServerController = __decorate([
61
65
  (0, common_1.Controller)('mcp'),
62
66
  __metadata("design:paramtypes", [mcp_server_service_1.McpServerService])
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-server.controller.js","sourceRoot":"","sources":["../lib/mcp-server/mcp-server.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAWwB;AAExB,+BAA2C;AAC3C,qCAA6C;AAE7C,6DAAwD;AAGjD,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAGD;IAFrB,YAAY,GAAG,IAAI,cAAO,EAAgB,CAAC;IAEnD,YAA6B,UAA4B;QAA5B,eAAU,GAAV,UAAU,CAAkB;IAAG,CAAC;IAO7D,GAAG,CAAQ,GAAY;QACrB,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CACT,kCAAkC,SAAS,EAAE,QAAQ,EAAE,IAAI,SAAS,EAAE,CACvE,CAAC;QAEF,OAAO,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,CAAC;IAC1C,CAAC;IAOK,AAAN,KAAK,CAAC,qBAAqB,CACjB,IAAoB,EACrB,GAAa;QAEpB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,GAAG,CAAC,MAAM,CAAC,mBAAU,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QAChD,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;YACjC,GAAG,CAAC,SAAS,CAAC,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,OAAO,GAAG,CAAC,MAAM,CAAC,mBAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;CAQF,CAAA;AA9CY,kDAAmB;AAU9B;IAFC,IAAA,YAAG,GAAE;IACL,IAAA,YAAG,GAAE;IACD,WAAA,IAAA,YAAG,GAAE,CAAA;;;oCAAgB,iBAAU;8CAOnC;AAOK;IAFL,IAAA,kBAAS,EAAC,0BAAiB,CAAC;IAC5B,IAAA,aAAI,GAAE;IAEJ,WAAA,IAAA,aAAI,GAAE,CAAA;IACN,WAAA,IAAA,YAAG,GAAE,CAAA;;;;gEAYP;8BAtCU,mBAAmB;IAD/B,IAAA,mBAAU,EAAC,KAAK,CAAC;qCAIyB,qCAAgB;GAH9C,mBAAmB,CA8C/B"}
1
+ {"version":3,"file":"mcp-server.controller.js","sourceRoot":"","sources":["../lib/mcp-server/mcp-server.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA6E;AAE7E,6DAAwD;AACxD,wEAAmE;AAG5D,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAGD;IAFZ,YAAY,GAAG,IAAI,GAAG,EAA4B,CAAC;IAEpE,YAA6B,UAA4B;QAA5B,eAAU,GAAV,UAAU,CAAkB;IAAG,CAAC;IAGvD,AAAN,KAAK,CAAC,GAAG,CAAQ,GAAY,EAAS,GAAa;QACjD,MAAM,SAAS,GAAG,IAAI,qCAAgB,CAAC,cAAc,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,sBAAsB,EAAE,CAAC;QAExD,SAAS,CAAC,OAAO,GAAG,GAAG,EAAE;YACvB,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAChD,CAAC,CAAC;QAEF,SAAS,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAEtD,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC;IAC3C,CAAC;IAGK,AAAN,KAAK,CAAC,aAAa,CAAQ,GAAY,EAAS,GAAa;QAC3D,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAW,CAAC;QACnD,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAEnD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,GAAG;iBACP,MAAM,CAAC,mBAAU,CAAC,SAAS,CAAC;iBAC5B,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;QAC1C,CAAC;QAED,MAAM,SAAS,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC;CACF,CAAA;AAlCY,kDAAmB;AAMxB;IADL,IAAA,YAAG,EAAC,KAAK,CAAC;IACA,WAAA,IAAA,YAAG,GAAE,CAAA;IAAgB,WAAA,IAAA,YAAG,GAAE,CAAA;;;;8CAapC;AAGK;IADL,IAAA,aAAI,EAAC,SAAS,CAAC;IACK,WAAA,IAAA,YAAG,GAAE,CAAA;IAAgB,WAAA,IAAA,YAAG,GAAE,CAAA;;;;wDAW9C;8BAjCU,mBAAmB;IAD/B,IAAA,mBAAU,EAAC,KAAK,CAAC;qCAIyB,qCAAgB;GAH9C,mBAAmB,CAkC/B"}
@@ -1,18 +1,7 @@
1
- import { JsonRpcErrorResult, JsonRpcRequest, JsonRpcResult } from './mcp-server.types';
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
2
  import { McpMetadataRegistryService } from './mcp-metadata-registry.service';
3
- type HandlerResult<T = unknown> = {
4
- result: JsonRpcResult<T> | JsonRpcErrorResult;
5
- } | null;
6
3
  export declare class McpServerService {
7
4
  private readonly registry;
8
- private readonly methodHandlers;
9
5
  constructor(registry: McpMetadataRegistryService);
10
- handleMessage(message: JsonRpcRequest): Promise<HandlerResult>;
11
- private initialize;
12
- private toolsList;
13
- private toolCall;
14
- private resourceList;
15
- private resourceRead;
16
- private wrapResult;
6
+ createConfiguredServer(): Server;
17
7
  }
18
- export {};
@@ -11,118 +11,70 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.McpServerService = void 0;
13
13
  const common_1 = require("@nestjs/common");
14
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
15
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
14
16
  const mcp_metadata_registry_service_1 = require("./mcp-metadata-registry.service");
15
17
  let McpServerService = class McpServerService {
16
18
  registry;
17
- methodHandlers = new Map([
18
- ['initialize', this.initialize.bind(this)],
19
- ['tools/list', this.toolsList.bind(this)],
20
- ['tools/call', this.toolCall.bind(this)],
21
- ['resources/list', this.resourceList.bind(this)],
22
- ['resources/read', this.resourceRead.bind(this)],
23
- ]);
24
19
  constructor(registry) {
25
20
  this.registry = registry;
26
21
  }
27
- async handleMessage(message) {
28
- const { method, id } = message;
29
- try {
30
- const methodHandler = this.methodHandlers.get(method);
31
- if (!methodHandler) {
32
- return null;
33
- }
34
- return methodHandler(message);
35
- }
36
- catch (error) {
37
- return {
38
- result: {
39
- jsonrpc: '2.0',
40
- id,
41
- error: {
42
- code: -32603,
43
- message: error instanceof Error ? error.message : 'Unknown error',
44
- },
45
- },
46
- };
47
- }
48
- }
49
- initialize({ id }) {
50
- return this.wrapResult(id, {
51
- protocolVersion: '2024-11-05',
52
- capabilities: {
53
- tools: { listChanged: true },
54
- resources: { subscribe: true, listChanged: true },
55
- },
56
- serverInfo: {
57
- name: 'nestjs-mcp-server',
58
- version: '1.0.0',
59
- },
60
- });
61
- }
62
- toolsList({ id }) {
63
- return this.wrapResult(id, {
22
+ createConfiguredServer() {
23
+ const server = new index_js_1.Server({ name: 'nestjs-mcp-server', version: '1.0.0' }, { capabilities: { tools: {}, resources: {} } });
24
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, () => ({
64
25
  tools: this.registry.getTools().map((t) => ({
65
26
  name: t.name,
66
27
  description: t.description,
67
- inputSchema: t.inputSchema || { type: 'object' },
28
+ inputSchema: t.inputSchema || {
29
+ type: 'object',
30
+ properties: {},
31
+ },
68
32
  })),
69
- });
70
- }
71
- async toolCall(message) {
72
- const { id, params } = message;
73
- const executor = this.registry.getToolExecutor(params.name);
74
- if (!executor) {
75
- throw new Error(`Tool not found: ${params.name}`);
76
- }
77
- const executionResult = await executor.execute(message);
78
- return this.wrapResult(id, {
79
- content: [
80
- {
81
- type: 'text',
82
- text: JSON.stringify(executionResult),
33
+ }));
34
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (req) => {
35
+ const executor = this.registry.getToolExecutor(req.params.name);
36
+ if (!executor) {
37
+ throw new Error(`Tool not found: ${req.params.name}`);
38
+ }
39
+ const result = await executor.execute({
40
+ jsonrpc: '2.0',
41
+ id: undefined,
42
+ method: 'tools/call',
43
+ params: {
44
+ name: req.params.name,
45
+ arguments: req.params.arguments ?? {},
83
46
  },
84
- ],
85
- isError: false,
47
+ });
48
+ return {
49
+ content: [{ type: 'text', text: JSON.stringify(result) }],
50
+ };
86
51
  });
87
- }
88
- resourceList({ id }) {
89
- return this.wrapResult(id, {
52
+ server.setRequestHandler(types_js_1.ListResourcesRequestSchema, () => ({
90
53
  resources: this.registry.getResources(),
91
- });
92
- }
93
- async resourceRead(message) {
94
- const { id, params } = message;
95
- const uri = params.uri;
96
- const matchResult = this.registry.getResourceHandler(uri);
97
- if (!matchResult) {
98
- throw new Error(`Resource not found: ${uri}`);
99
- }
100
- const { handler, params: uriParams } = matchResult;
101
- const transformedRequest = {
102
- ...message,
103
- params: {
104
- arguments: uriParams,
105
- },
106
- };
107
- const executionResult = await handler(transformedRequest);
108
- return this.wrapResult(id, {
109
- contents: [
110
- {
111
- uri,
112
- mimeType: 'application/json',
113
- text: JSON.stringify(executionResult),
114
- },
115
- ],
116
- });
117
- }
118
- wrapResult(id, result) {
119
- return {
120
- result: {
54
+ }));
55
+ server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (req) => {
56
+ const uri = req.params.uri;
57
+ const match = this.registry.getResourceHandler(uri);
58
+ if (!match) {
59
+ throw new Error(`Resource not found: ${uri}`);
60
+ }
61
+ const result = await match.handler({
121
62
  jsonrpc: '2.0',
122
- id,
123
- result,
124
- },
125
- };
63
+ id: undefined,
64
+ method: 'resources/read',
65
+ params: { arguments: match.params },
66
+ });
67
+ return {
68
+ contents: [
69
+ {
70
+ uri,
71
+ mimeType: 'application/json',
72
+ text: JSON.stringify(result),
73
+ },
74
+ ],
75
+ };
76
+ });
77
+ return server;
126
78
  }
127
79
  };
128
80
  exports.McpServerService = McpServerService;
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-server.service.js","sourceRoot":"","sources":["../lib/mcp-server/mcp-server.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mcp-server.service.js","sourceRoot":"","sources":["../lib/mcp-server/mcp-server.service.ts"],"names":[],"mappings":"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"}
@@ -8,11 +8,9 @@ export interface McpServerConfig {
8
8
  export interface McpServerFeatureOptions extends Pick<ModuleMetadata, 'imports'> {
9
9
  executors: Provider<BaseExecutor>[];
10
10
  }
11
- export interface CommonJsonRpc {
11
+ export interface JsonRpcRequest<T = any> {
12
12
  jsonrpc: '2.0';
13
13
  id?: number | string;
14
- }
15
- export interface JsonRpcRequest<T = any> extends CommonJsonRpc {
16
14
  method: string;
17
15
  params: T;
18
16
  }
@@ -20,12 +18,3 @@ export type JsonRpcToolRequest<T = unknown> = JsonRpcRequest<{
20
18
  name: string;
21
19
  arguments: T;
22
20
  }>;
23
- export interface JsonRpcResourceParams {
24
- uri: string;
25
- }
26
- export interface JsonRpcResult<T = unknown> extends CommonJsonRpc {
27
- result: T;
28
- }
29
- export interface JsonRpcErrorResult<T = unknown> extends CommonJsonRpc {
30
- error: T;
31
- }
@@ -0,0 +1,18 @@
1
+ import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
2
+ import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
3
+ import type { Request, Response } from 'express';
4
+ export declare class SseHttpTransport implements Transport {
5
+ private readonly _endpoint;
6
+ private _sessionId;
7
+ private _res;
8
+ onclose?: () => void;
9
+ onerror?: (error: Error) => void;
10
+ onmessage?: (message: JSONRPCMessage) => void;
11
+ constructor(_endpoint: string);
12
+ get sessionId(): string;
13
+ attachResponse(res: Response): void;
14
+ start(): Promise<void>;
15
+ send(message: JSONRPCMessage): Promise<void>;
16
+ close(): Promise<void>;
17
+ handlePostMessage(req: Request, res: Response): Promise<void>;
18
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SseHttpTransport = void 0;
4
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
5
+ const node_crypto_1 = require("node:crypto");
6
+ class SseHttpTransport {
7
+ _endpoint;
8
+ _sessionId;
9
+ _res = null;
10
+ onclose;
11
+ onerror;
12
+ onmessage;
13
+ constructor(_endpoint) {
14
+ this._endpoint = _endpoint;
15
+ this._sessionId = (0, node_crypto_1.randomUUID)();
16
+ }
17
+ get sessionId() {
18
+ return this._sessionId;
19
+ }
20
+ attachResponse(res) {
21
+ this._res = res;
22
+ res.writeHead(200, {
23
+ 'Content-Type': 'text/event-stream',
24
+ 'Cache-Control': 'no-cache, no-transform',
25
+ Connection: 'keep-alive',
26
+ });
27
+ res.write(`event: endpoint\ndata: ${this._endpoint}?sessionId=${this._sessionId}\n\n`);
28
+ res.on('close', () => {
29
+ this._res = null;
30
+ this.onclose?.();
31
+ });
32
+ }
33
+ async start() {
34
+ }
35
+ async send(message) {
36
+ if (!this._res) {
37
+ throw new Error('SSE connection not established');
38
+ }
39
+ this._res.write(`event: message\ndata: ${JSON.stringify(message)}\n\n`);
40
+ }
41
+ async close() {
42
+ this._res?.end();
43
+ this._res = null;
44
+ this.onclose?.();
45
+ }
46
+ async handlePostMessage(req, res) {
47
+ if (!this._res) {
48
+ res.writeHead(500).end('SSE connection not established');
49
+ return;
50
+ }
51
+ let body;
52
+ try {
53
+ body = req.body;
54
+ if (!body || typeof body !== 'object') {
55
+ throw new Error('Invalid body');
56
+ }
57
+ }
58
+ catch (error) {
59
+ res.writeHead(400).end(String(error));
60
+ this.onerror?.(error);
61
+ return;
62
+ }
63
+ try {
64
+ const message = types_js_1.JSONRPCMessageSchema.parse(body);
65
+ this.onmessage?.(message);
66
+ }
67
+ catch (error) {
68
+ res.writeHead(400).end(`Invalid message: ${JSON.stringify(body)}`);
69
+ this.onerror?.(error);
70
+ return;
71
+ }
72
+ res.writeHead(202).end('Accepted');
73
+ }
74
+ }
75
+ exports.SseHttpTransport = SseHttpTransport;
76
+ //# sourceMappingURL=sse-http-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse-http-transport.js","sourceRoot":"","sources":["../../lib/mcp-server/transports/sse-http-transport.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jissp/nestjs-mcp-server",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "NestJS-based MCP Server with metadata scanning support",
5
5
  "author": "jissp <jisspant@gmail.com>",
6
6
  "private": false,
@@ -50,7 +50,9 @@
50
50
  "rxjs": "^7.8.1"
51
51
  },
52
52
  "dependencies": {
53
- "@jissp/metadata-scanner": "^0.0.4"
53
+ "@jissp/metadata-scanner": "^0.0.4",
54
+ "@modelcontextprotocol/sdk": "^1.29.0",
55
+ "@nestjs/platform-express": "^11.1.18"
54
56
  },
55
57
  "devDependencies": {
56
58
  "@eslint/eslintrc": "^3.2.0",