@theorvane/type-mcp 0.2.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.
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/dist/chunk-64DS4UNF.js +106 -0
- package/dist/chunk-64DS4UNF.js.map +1 -0
- package/dist/http.cjs +114 -0
- package/dist/http.cjs.map +1 -0
- package/dist/http.d.cts +24 -0
- package/dist/http.d.ts +24 -0
- package/dist/http.js +89 -0
- package/dist/http.js.map +1 -0
- package/dist/index.cjs +438 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +51 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +318 -0
- package/dist/index.js.map +1 -0
- package/dist/instance-resolver-BdKelIch.d.cts +46 -0
- package/dist/instance-resolver-BdKelIch.d.ts +46 -0
- package/dist/langchain.cjs +161 -0
- package/dist/langchain.cjs.map +1 -0
- package/dist/langchain.d.cts +11 -0
- package/dist/langchain.d.ts +11 -0
- package/dist/langchain.js +43 -0
- package/dist/langchain.js.map +1 -0
- package/package.json +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 sjungwon03
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="docs/assets/type-mcp-hero.png" alt="Abstract decorator tiles flowing through a modular core toward web and dependency integrations" width="100%" />
|
|
3
|
+
|
|
4
|
+
<h1>TypeMCP</h1>
|
|
5
|
+
|
|
6
|
+
**Decorator-first MCP declarations for strict TypeScript.**
|
|
7
|
+
|
|
8
|
+
[](https://www.npmjs.com/package/@theorvane/type-mcp)
|
|
9
|
+
[](package.json)
|
|
10
|
+
[](https://modelcontextprotocol.io/)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
> **Release target — `@theorvane/type-mcp@0.2.0`:** this repository source contains standard decorators, definition validation, explicit instance resolution, MCP SDK compilation, stdio, `@theorvane/type-mcp/http` Streamable HTTP, and the tools-only `@theorvane/type-mcp/langchain` adapter. Before installing, confirm the version exists with `npm view @theorvane/type-mcp@0.2.0 version`.
|
|
15
|
+
>
|
|
16
|
+
> **Integration boundary:** LangGraph `ToolNode` composition, graph topology, model choice, authorization, state, persistence, and deployment remain consumer responsibilities.
|
|
17
|
+
|
|
18
|
+
TypeMCP keeps MCP declarations beside TypeScript classes without coupling the core to a web framework. Install it when you need a strict, inspectable declaration layer and want application code ready for later runtime support.
|
|
19
|
+
|
|
20
|
+
## Fast path for developers and agents
|
|
21
|
+
|
|
22
|
+
1. Check the published capability table below. Do not call an API marked **reserved** or **planned**.
|
|
23
|
+
2. After the release version is visible in npm, install [`@theorvane/type-mcp`](https://www.npmjs.com/package/@theorvane/type-mcp) with `zod`.
|
|
24
|
+
3. Use standard TypeScript decorators to declare a server surface.
|
|
25
|
+
4. Inspect the declaration through `getMcpServerDefinition()` at an application boundary.
|
|
26
|
+
5. Use `createMcpServer()`, `startStdioServer()`, or `@theorvane/type-mcp/http` only when the application owns the surrounding transport, authorization, and lifecycle policy.
|
|
27
|
+
|
|
28
|
+
Agents should start with [the agent integration guide](docs/guides/agent-integration.md). It defines an evidence-first workflow and prevents unavailable runtime APIs from being mistaken for supported features.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
TypeMCP requires **Node.js 20 or later** and TypeScript with standard (Stage 3) decorator support.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @theorvane/type-mcp zod
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The package is ESM-first and also exposes a CommonJS root export. TypeScript projects should use Node-aware module resolution. This `tsconfig.json` baseline matches the package contract:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"compilerOptions": {
|
|
43
|
+
"target": "ES2022",
|
|
44
|
+
"module": "NodeNext",
|
|
45
|
+
"moduleResolution": "NodeNext",
|
|
46
|
+
"lib": ["ES2022", "ESNext.Decorators"],
|
|
47
|
+
"strict": true,
|
|
48
|
+
"verbatimModuleSyntax": true
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Do not enable TypeScript's legacy `experimentalDecorators` mode for these standard decorator examples. See [configuration and compatibility](docs/guides/configuration.md) for ESM, CommonJS, and decorator details.
|
|
54
|
+
|
|
55
|
+
## Define and inspect a server declaration
|
|
56
|
+
|
|
57
|
+
Create `src/catalog-server.ts`:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { z } from "zod";
|
|
61
|
+
import {
|
|
62
|
+
getMcpServerDefinition,
|
|
63
|
+
McpPrompt,
|
|
64
|
+
McpResource,
|
|
65
|
+
McpServer,
|
|
66
|
+
McpTool,
|
|
67
|
+
} from "@theorvane/type-mcp";
|
|
68
|
+
|
|
69
|
+
@McpServer({ name: "catalog", version: "0.2.0" })
|
|
70
|
+
export class CatalogServer {
|
|
71
|
+
@McpTool({
|
|
72
|
+
description: "Look up a catalog item by SKU.",
|
|
73
|
+
input: z.object({ sku: z.string().min(1) }),
|
|
74
|
+
})
|
|
75
|
+
findProduct({ sku }: { sku: string }) {
|
|
76
|
+
return { sku, available: true };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@McpResource({
|
|
80
|
+
uri: "config://catalog",
|
|
81
|
+
mimeType: "application/json",
|
|
82
|
+
description: "Static catalog configuration.",
|
|
83
|
+
})
|
|
84
|
+
readConfig() {
|
|
85
|
+
return { region: "ap-northeast-2" };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@McpPrompt({ description: "Prepare a product summary request." })
|
|
89
|
+
summarizeProduct() {
|
|
90
|
+
return "Summarize the selected catalog product.";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const definition = getMcpServerDefinition(CatalogServer);
|
|
95
|
+
console.log(definition?.name); // "catalog"
|
|
96
|
+
console.log(definition?.tools[0]?.name); // "findProduct"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`getMcpServerDefinition()` returns `undefined` for a class without `@McpServer`. For a decorated class, it returns a newly allocated frozen metadata container on every call. Zod schemas retain their original identity, so treat a schema passed to a decorator as immutable after declaration.
|
|
100
|
+
|
|
101
|
+
The methods above are ordinary application methods. In `0.2.0`, use `createMcpServer()` to validate and compile this declaration through an explicit resolver; choose an adapter exported by the installed package only when the application owns its hosting, authorization, and lifecycle policy. Follow the [getting-started guide](docs/guides/getting-started.md) for the complete version boundary.
|
|
102
|
+
|
|
103
|
+
## Capability map
|
|
104
|
+
|
|
105
|
+
| Surface | `@theorvane/type-mcp@0.2.0` | What it does |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| `@McpServer` | Available | Records server name and version metadata. |
|
|
108
|
+
| `@McpTool` | Available | Records a method name, optional public name/description, and Zod object schema. |
|
|
109
|
+
| `@McpResource` | Available | Records a static resource URI and optional metadata. |
|
|
110
|
+
| `@McpPrompt` | Available | Records a named prompt declaration. |
|
|
111
|
+
| `getMcpServerDefinition()` | Available | Reads a fresh frozen metadata copy; returns `undefined` for undecorated classes. |
|
|
112
|
+
| `createMcpServer()` | Available | Validates declarations and compiles the decorated server surface with an explicit resolver seam. |
|
|
113
|
+
| `@theorvane/type-mcp/http` / `createMcpHandler()` | Available | Fetch/Streamable HTTP adapter; applications own hosting, sessions, and authorization. |
|
|
114
|
+
| Definition validation and `TypeMcpDefinitionError` | Available | Validates declarations and reports safe definition errors. |
|
|
115
|
+
| `InstanceResolver<T>` / `resolveMcpServerInstance()` | Available | Explicit application-owned instance construction contract. |
|
|
116
|
+
| `@theorvane/type-mcp/langchain` / `createLangChainTools()` | Available | Tools-only LangChain structured-tool adapter; LangGraph `ToolNode` composition remains consumer-owned. |
|
|
117
|
+
|
|
118
|
+
## Documentation map
|
|
119
|
+
|
|
120
|
+
- [Getting started](docs/guides/getting-started.md) — install, declare, inspect, and compile a TypeMCP server.
|
|
121
|
+
- [Configuration and compatibility](docs/guides/configuration.md) — Node, ESM/CommonJS, TypeScript decorators, schemas, and release boundaries.
|
|
122
|
+
- [Agent integration guide](docs/guides/agent-integration.md) — evidence-first coding-agent workflow and explicit runtime boundaries.
|
|
123
|
+
- [HTTP framework integration](docs/guides/http-and-nextjs.md) — published Streamable HTTP example and Fetch/Next.js route shape.
|
|
124
|
+
- [Standalone HTTP example](examples/standalone-http/README.md) — exact source and smoke-test commands for the repository implementation.
|
|
125
|
+
- [LangChain and LangGraph integration](docs/guides/langchain-langgraph.md) — published tools-only adapter and consumer-owned `ToolNode` composition.
|
|
126
|
+
- [LangGraph ToolNode example](examples/langgraph-tools/README.md) — exact in-memory source example and smoke-test command.
|
|
127
|
+
- [Decorator API contract](docs/api/decorator-api.md) — repository API target; check its status notices before using unreleased APIs.
|
|
128
|
+
- [Architecture overview](docs/architecture/overview.md) — package boundaries and planned runtime direction.
|
|
129
|
+
- [MVP scope](docs/product/mvp-scope.md) — planned product capabilities.
|
|
130
|
+
- [Contributing](CONTRIBUTING.md) — contribution workflow and local verification.
|
|
131
|
+
- [npm package](https://www.npmjs.com/package/@theorvane/type-mcp) — published releases and install metadata.
|
|
132
|
+
|
|
133
|
+
## Develop locally
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
git clone https://github.com/Theorvane/type-mcp.git
|
|
137
|
+
cd type-mcp
|
|
138
|
+
npm ci
|
|
139
|
+
npm run lint
|
|
140
|
+
npm run typecheck
|
|
141
|
+
npm test
|
|
142
|
+
npm run build
|
|
143
|
+
npm run verify:package
|
|
144
|
+
npm run verify:publish
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Repository changes follow **Issue → issue-numbered branch → pull request → review and CI → squash merge**. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var TypeMcpDefinitionError = class extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "TypeMcpDefinitionError";
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// src/metadata/definitions.ts
|
|
10
|
+
var serverDefinitions = /* @__PURE__ */ new WeakMap();
|
|
11
|
+
function storeMcpServerDefinition(target, definition) {
|
|
12
|
+
serverDefinitions.set(target, freezeServerDefinition(definition));
|
|
13
|
+
}
|
|
14
|
+
function getMcpServerDefinition(target) {
|
|
15
|
+
const definition = serverDefinitions.get(target);
|
|
16
|
+
return definition === void 0 ? void 0 : freezeServerDefinition(definition);
|
|
17
|
+
}
|
|
18
|
+
function freezeServerDefinition(definition) {
|
|
19
|
+
return Object.freeze({
|
|
20
|
+
name: definition.name,
|
|
21
|
+
version: definition.version,
|
|
22
|
+
tools: Object.freeze(
|
|
23
|
+
definition.tools.map(
|
|
24
|
+
(tool) => Object.freeze({
|
|
25
|
+
name: tool.name,
|
|
26
|
+
methodName: tool.methodName,
|
|
27
|
+
description: tool.description,
|
|
28
|
+
input: tool.input
|
|
29
|
+
})
|
|
30
|
+
)
|
|
31
|
+
),
|
|
32
|
+
resources: Object.freeze(
|
|
33
|
+
definition.resources.map(
|
|
34
|
+
(resource) => Object.freeze({
|
|
35
|
+
name: resource.name,
|
|
36
|
+
methodName: resource.methodName,
|
|
37
|
+
uri: resource.uri,
|
|
38
|
+
mimeType: resource.mimeType,
|
|
39
|
+
description: resource.description
|
|
40
|
+
})
|
|
41
|
+
)
|
|
42
|
+
),
|
|
43
|
+
prompts: Object.freeze(
|
|
44
|
+
definition.prompts.map(
|
|
45
|
+
(prompt) => Object.freeze({
|
|
46
|
+
name: prompt.name,
|
|
47
|
+
methodName: prompt.methodName,
|
|
48
|
+
description: prompt.description
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/metadata/read-server-definition.ts
|
|
56
|
+
function readMcpServerDefinition(target) {
|
|
57
|
+
const definition = getMcpServerDefinition(target);
|
|
58
|
+
const className = target.name || "<anonymous>";
|
|
59
|
+
if (definition === void 0) {
|
|
60
|
+
throw new TypeMcpDefinitionError(
|
|
61
|
+
`MCP server definition is missing for ${className}`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
assertUniqueNames("tool", definition.tools, className);
|
|
65
|
+
assertUniqueNames("resource", definition.resources, className);
|
|
66
|
+
assertUniqueNames("prompt", definition.prompts, className);
|
|
67
|
+
return definition;
|
|
68
|
+
}
|
|
69
|
+
function assertUniqueNames(componentType, definitions, className) {
|
|
70
|
+
const names = /* @__PURE__ */ new Set();
|
|
71
|
+
for (const definition of definitions) {
|
|
72
|
+
if (names.has(definition.name)) {
|
|
73
|
+
throw new TypeMcpDefinitionError(
|
|
74
|
+
`Duplicate MCP ${componentType} name "${definition.name}" on ${className}`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
names.add(definition.name);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/resolver/default-instance-resolver.ts
|
|
82
|
+
var defaultInstanceResolver = {
|
|
83
|
+
resolve(serverClass) {
|
|
84
|
+
return new serverClass();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/resolver/resolve-server-instance.ts
|
|
89
|
+
async function resolveMcpServerInstance(serverClass, resolver) {
|
|
90
|
+
if (resolver) {
|
|
91
|
+
return resolver.resolve(serverClass);
|
|
92
|
+
}
|
|
93
|
+
return defaultInstanceResolver.resolve(
|
|
94
|
+
serverClass
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export {
|
|
99
|
+
TypeMcpDefinitionError,
|
|
100
|
+
storeMcpServerDefinition,
|
|
101
|
+
getMcpServerDefinition,
|
|
102
|
+
readMcpServerDefinition,
|
|
103
|
+
defaultInstanceResolver,
|
|
104
|
+
resolveMcpServerInstance
|
|
105
|
+
};
|
|
106
|
+
//# sourceMappingURL=chunk-64DS4UNF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/metadata/definitions.ts","../src/metadata/read-server-definition.ts","../src/resolver/default-instance-resolver.ts","../src/resolver/resolve-server-instance.ts"],"sourcesContent":["export class TypeMcpDefinitionError extends Error {\n\tpublic constructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"TypeMcpDefinitionError\";\n\t}\n}\n","import type {\n\tMcpPromptDefinition,\n\tMcpResourceDefinition,\n\tMcpServerConstructor,\n\tMcpServerDefinition,\n\tMcpToolDefinition,\n} from \"../types.js\";\n\nexport interface PendingMcpDefinitions {\n\treadonly tools: readonly McpToolDefinition[];\n\treadonly resources: readonly McpResourceDefinition[];\n\treadonly prompts: readonly McpPromptDefinition[];\n}\n\nconst serverDefinitions = new WeakMap<object, McpServerDefinition>();\n\nexport function storeMcpServerDefinition<\n\tT extends object,\n\tArguments extends readonly unknown[],\n>(\n\ttarget: McpServerConstructor<T, Arguments>,\n\tdefinition: McpServerDefinition,\n): void {\n\tserverDefinitions.set(target, freezeServerDefinition(definition));\n}\n\nexport function getMcpServerDefinition<\n\tT extends object,\n\tArguments extends readonly unknown[],\n>(target: McpServerConstructor<T, Arguments>): McpServerDefinition | undefined {\n\tconst definition = serverDefinitions.get(target);\n\treturn definition === undefined\n\t\t? undefined\n\t\t: freezeServerDefinition(definition);\n}\n\nexport function freezeServerDefinition(\n\tdefinition: McpServerDefinition,\n): McpServerDefinition {\n\treturn Object.freeze({\n\t\tname: definition.name,\n\t\tversion: definition.version,\n\t\ttools: Object.freeze(\n\t\t\tdefinition.tools.map((tool) =>\n\t\t\t\tObject.freeze({\n\t\t\t\t\tname: tool.name,\n\t\t\t\t\tmethodName: tool.methodName,\n\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\tinput: tool.input,\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t\tresources: Object.freeze(\n\t\t\tdefinition.resources.map((resource) =>\n\t\t\t\tObject.freeze({\n\t\t\t\t\tname: resource.name,\n\t\t\t\t\tmethodName: resource.methodName,\n\t\t\t\t\turi: resource.uri,\n\t\t\t\t\tmimeType: resource.mimeType,\n\t\t\t\t\tdescription: resource.description,\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t\tprompts: Object.freeze(\n\t\t\tdefinition.prompts.map((prompt) =>\n\t\t\t\tObject.freeze({\n\t\t\t\t\tname: prompt.name,\n\t\t\t\t\tmethodName: prompt.methodName,\n\t\t\t\t\tdescription: prompt.description,\n\t\t\t\t}),\n\t\t\t),\n\t\t),\n\t});\n}\n","import { TypeMcpDefinitionError } from \"../errors.js\";\nimport type {\n\tMcpPromptDefinition,\n\tMcpResourceDefinition,\n\tMcpServerConstructor,\n\tMcpServerDefinition,\n\tMcpToolDefinition,\n} from \"../types.js\";\nimport { getMcpServerDefinition } from \"./definitions.js\";\n\ntype NamedDefinition =\n\t| McpToolDefinition\n\t| McpResourceDefinition\n\t| McpPromptDefinition;\n\nexport function readMcpServerDefinition<\n\tT extends object,\n\tArguments extends readonly unknown[],\n>(target: McpServerConstructor<T, Arguments>): McpServerDefinition {\n\tconst definition = getMcpServerDefinition(target);\n\tconst className = target.name || \"<anonymous>\";\n\n\tif (definition === undefined) {\n\t\tthrow new TypeMcpDefinitionError(\n\t\t\t`MCP server definition is missing for ${className}`,\n\t\t);\n\t}\n\n\tassertUniqueNames(\"tool\", definition.tools, className);\n\tassertUniqueNames(\"resource\", definition.resources, className);\n\tassertUniqueNames(\"prompt\", definition.prompts, className);\n\n\treturn definition;\n}\n\nfunction assertUniqueNames(\n\tcomponentType: \"tool\" | \"resource\" | \"prompt\",\n\tdefinitions: readonly NamedDefinition[],\n\tclassName: string,\n): void {\n\tconst names = new Set<string>();\n\n\tfor (const definition of definitions) {\n\t\tif (names.has(definition.name)) {\n\t\t\tthrow new TypeMcpDefinitionError(\n\t\t\t\t`Duplicate MCP ${componentType} name \"${definition.name}\" on ${className}`,\n\t\t\t);\n\t\t}\n\n\t\tnames.add(definition.name);\n\t}\n}\n","import type { ZeroArgumentMcpServerConstructor } from \"../types.js\";\n\nexport const defaultInstanceResolver = {\n\tresolve<T extends object>(\n\t\tserverClass: ZeroArgumentMcpServerConstructor<T>,\n\t): T {\n\t\treturn new serverClass();\n\t},\n};\n","import type {\n\tMcpServerConstructor,\n\tZeroArgumentMcpServerConstructor,\n} from \"../types.js\";\nimport { defaultInstanceResolver } from \"./default-instance-resolver.js\";\nimport type { InstanceResolver } from \"./instance-resolver.js\";\n\nexport function resolveMcpServerInstance<T extends object>(\n\tserverClass: ZeroArgumentMcpServerConstructor<T>,\n): Promise<T>;\nexport function resolveMcpServerInstance<\n\tT extends object,\n\tArguments extends readonly unknown[],\n>(\n\tserverClass: McpServerConstructor<T, Arguments>,\n\tresolver: InstanceResolver<T>,\n): Promise<T>;\nexport async function resolveMcpServerInstance<\n\tT extends object,\n\tArguments extends readonly unknown[],\n>(\n\tserverClass: McpServerConstructor<T, Arguments>,\n\tresolver?: InstanceResolver<T>,\n): Promise<T> {\n\tif (resolver) {\n\t\treturn resolver.resolve(serverClass);\n\t}\n\n\treturn defaultInstanceResolver.resolve(\n\t\tserverClass as ZeroArgumentMcpServerConstructor<T>,\n\t);\n}\n"],"mappings":";AAAO,IAAM,yBAAN,cAAqC,MAAM;AAAA,EAC1C,YAAY,SAAiB;AACnC,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACb;AACD;;;ACSA,IAAM,oBAAoB,oBAAI,QAAqC;AAE5D,SAAS,yBAIf,QACA,YACO;AACP,oBAAkB,IAAI,QAAQ,uBAAuB,UAAU,CAAC;AACjE;AAEO,SAAS,uBAGd,QAA6E;AAC9E,QAAM,aAAa,kBAAkB,IAAI,MAAM;AAC/C,SAAO,eAAe,SACnB,SACA,uBAAuB,UAAU;AACrC;AAEO,SAAS,uBACf,YACsB;AACtB,SAAO,OAAO,OAAO;AAAA,IACpB,MAAM,WAAW;AAAA,IACjB,SAAS,WAAW;AAAA,IACpB,OAAO,OAAO;AAAA,MACb,WAAW,MAAM;AAAA,QAAI,CAAC,SACrB,OAAO,OAAO;AAAA,UACb,MAAM,KAAK;AAAA,UACX,YAAY,KAAK;AAAA,UACjB,aAAa,KAAK;AAAA,UAClB,OAAO,KAAK;AAAA,QACb,CAAC;AAAA,MACF;AAAA,IACD;AAAA,IACA,WAAW,OAAO;AAAA,MACjB,WAAW,UAAU;AAAA,QAAI,CAAC,aACzB,OAAO,OAAO;AAAA,UACb,MAAM,SAAS;AAAA,UACf,YAAY,SAAS;AAAA,UACrB,KAAK,SAAS;AAAA,UACd,UAAU,SAAS;AAAA,UACnB,aAAa,SAAS;AAAA,QACvB,CAAC;AAAA,MACF;AAAA,IACD;AAAA,IACA,SAAS,OAAO;AAAA,MACf,WAAW,QAAQ;AAAA,QAAI,CAAC,WACvB,OAAO,OAAO;AAAA,UACb,MAAM,OAAO;AAAA,UACb,YAAY,OAAO;AAAA,UACnB,aAAa,OAAO;AAAA,QACrB,CAAC;AAAA,MACF;AAAA,IACD;AAAA,EACD,CAAC;AACF;;;AC1DO,SAAS,wBAGd,QAAiE;AAClE,QAAM,aAAa,uBAAuB,MAAM;AAChD,QAAM,YAAY,OAAO,QAAQ;AAEjC,MAAI,eAAe,QAAW;AAC7B,UAAM,IAAI;AAAA,MACT,wCAAwC,SAAS;AAAA,IAClD;AAAA,EACD;AAEA,oBAAkB,QAAQ,WAAW,OAAO,SAAS;AACrD,oBAAkB,YAAY,WAAW,WAAW,SAAS;AAC7D,oBAAkB,UAAU,WAAW,SAAS,SAAS;AAEzD,SAAO;AACR;AAEA,SAAS,kBACR,eACA,aACA,WACO;AACP,QAAM,QAAQ,oBAAI,IAAY;AAE9B,aAAW,cAAc,aAAa;AACrC,QAAI,MAAM,IAAI,WAAW,IAAI,GAAG;AAC/B,YAAM,IAAI;AAAA,QACT,iBAAiB,aAAa,UAAU,WAAW,IAAI,QAAQ,SAAS;AAAA,MACzE;AAAA,IACD;AAEA,UAAM,IAAI,WAAW,IAAI;AAAA,EAC1B;AACD;;;ACjDO,IAAM,0BAA0B;AAAA,EACtC,QACC,aACI;AACJ,WAAO,IAAI,YAAY;AAAA,EACxB;AACD;;;ACSA,eAAsB,yBAIrB,aACA,UACa;AACb,MAAI,UAAU;AACb,WAAO,SAAS,QAAQ,WAAW;AAAA,EACpC;AAEA,SAAO,wBAAwB;AAAA,IAC9B;AAAA,EACD;AACD;","names":[]}
|
package/dist/http.cjs
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/http.ts
|
|
21
|
+
var http_exports = {};
|
|
22
|
+
__export(http_exports, {
|
|
23
|
+
createMcpHandler: () => createMcpHandler
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(http_exports);
|
|
26
|
+
var import_webStandardStreamableHttp = require("@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js");
|
|
27
|
+
function mcpError(status, code, message) {
|
|
28
|
+
return Response.json(
|
|
29
|
+
{
|
|
30
|
+
jsonrpc: "2.0",
|
|
31
|
+
error: { code, message },
|
|
32
|
+
id: null
|
|
33
|
+
},
|
|
34
|
+
{ status }
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
async function isInitializeRequest(request) {
|
|
38
|
+
if (request.method !== "POST") {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const body = await request.clone().json();
|
|
43
|
+
return typeof body === "object" && body !== null && "method" in body && body.method === "initialize";
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function createMcpHandler(createServer, options = {}) {
|
|
49
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
50
|
+
async function createSession() {
|
|
51
|
+
const server = await createServer();
|
|
52
|
+
let session;
|
|
53
|
+
const transport = new import_webStandardStreamableHttp.WebStandardStreamableHTTPServerTransport({
|
|
54
|
+
sessionIdGenerator: () => crypto.randomUUID(),
|
|
55
|
+
enableJsonResponse: options.enableJsonResponse ?? true,
|
|
56
|
+
onsessioninitialized: (sessionId) => {
|
|
57
|
+
sessions.set(sessionId, session);
|
|
58
|
+
},
|
|
59
|
+
onsessionclosed: async (sessionId) => {
|
|
60
|
+
sessions.delete(sessionId);
|
|
61
|
+
await server.close();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
session = { server, transport };
|
|
65
|
+
try {
|
|
66
|
+
await server.connect(transport);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
try {
|
|
69
|
+
await server.close();
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
return session;
|
|
75
|
+
}
|
|
76
|
+
return async (request) => {
|
|
77
|
+
const sessionId = request.headers.get("mcp-session-id");
|
|
78
|
+
if (sessionId !== null) {
|
|
79
|
+
const session2 = sessions.get(sessionId);
|
|
80
|
+
return session2 === void 0 ? mcpError(404, -32001, "Session not found") : session2.transport.handleRequest(request);
|
|
81
|
+
}
|
|
82
|
+
if (request.method !== "POST") {
|
|
83
|
+
return new Response(null, { status: 405 });
|
|
84
|
+
}
|
|
85
|
+
if (!await isInitializeRequest(request)) {
|
|
86
|
+
return mcpError(400, -32e3, "Mcp-Session-Id header is required");
|
|
87
|
+
}
|
|
88
|
+
const session = await createSession();
|
|
89
|
+
try {
|
|
90
|
+
const response = await session.transport.handleRequest(request);
|
|
91
|
+
if (response.status < 400) {
|
|
92
|
+
return response;
|
|
93
|
+
}
|
|
94
|
+
const sessionId2 = session.transport.sessionId;
|
|
95
|
+
if (sessionId2 !== void 0) {
|
|
96
|
+
sessions.delete(sessionId2);
|
|
97
|
+
}
|
|
98
|
+
await session.server.close();
|
|
99
|
+
return response;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
const sessionId2 = session.transport.sessionId;
|
|
102
|
+
if (sessionId2 !== void 0) {
|
|
103
|
+
sessions.delete(sessionId2);
|
|
104
|
+
}
|
|
105
|
+
await session.server.close();
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
111
|
+
0 && (module.exports = {
|
|
112
|
+
createMcpHandler
|
|
113
|
+
});
|
|
114
|
+
//# sourceMappingURL=http.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/http.ts"],"sourcesContent":["import { WebStandardStreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\n\nexport interface McpHttpServerConnection {\n\tconnect(transport: Transport): Promise<void>;\n\tclose(): Promise<void>;\n}\n\nexport type McpHandler = (request: Request) => Promise<Response>;\n\nexport interface McpHandlerOptions {\n\t/**\n\t * Returns JSON responses instead of SSE streams for request/response MCP calls.\n\t * Defaults to true for a simple request/response Fetch handler.\n\t */\n\treadonly enableJsonResponse?: boolean;\n}\n\ninterface Session<Server extends McpHttpServerConnection> {\n\treadonly server: Server;\n\treadonly transport: WebStandardStreamableHTTPServerTransport;\n}\n\nfunction mcpError(status: number, code: number, message: string): Response {\n\treturn Response.json(\n\t\t{\n\t\t\tjsonrpc: \"2.0\",\n\t\t\terror: { code, message },\n\t\t\tid: null,\n\t\t},\n\t\t{ status },\n\t);\n}\n\nasync function isInitializeRequest(request: Request): Promise<boolean> {\n\tif (request.method !== \"POST\") {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\tconst body: unknown = await request.clone().json();\n\t\treturn (\n\t\t\ttypeof body === \"object\" &&\n\t\t\tbody !== null &&\n\t\t\t\"method\" in body &&\n\t\t\tbody.method === \"initialize\"\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Creates a Fetch-compatible Streamable HTTP handler with SDK-managed sessions.\n *\n * Each stateful MCP session receives its own compiled server and official Web\n * Standard transport. The adapter routes sessions before delegating valid MCP\n * traffic, HTTP methods, JSON-RPC framing, and lifecycle behavior to the SDK.\n */\nexport function createMcpHandler<Server extends McpHttpServerConnection>(\n\tcreateServer: () => Server | Promise<Server>,\n\toptions: McpHandlerOptions = {},\n): McpHandler {\n\tconst sessions = new Map<string, Session<Server>>();\n\n\tasync function createSession(): Promise<Session<Server>> {\n\t\tconst server = await createServer();\n\t\tlet session: Session<Server>;\n\t\tconst transport = new WebStandardStreamableHTTPServerTransport({\n\t\t\tsessionIdGenerator: () => crypto.randomUUID(),\n\t\t\tenableJsonResponse: options.enableJsonResponse ?? true,\n\t\t\tonsessioninitialized: (sessionId) => {\n\t\t\t\tsessions.set(sessionId, session);\n\t\t\t},\n\t\t\tonsessionclosed: async (sessionId) => {\n\t\t\t\tsessions.delete(sessionId);\n\t\t\t\tawait server.close();\n\t\t\t},\n\t\t});\n\t\tsession = { server, transport };\n\t\ttry {\n\t\t\tawait server.connect(transport);\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tawait server.close();\n\t\t\t} catch {\n\t\t\t\t// Preserve the original transport connection failure.\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\treturn session;\n\t}\n\n\treturn async (request) => {\n\t\tconst sessionId = request.headers.get(\"mcp-session-id\");\n\t\tif (sessionId !== null) {\n\t\t\tconst session = sessions.get(sessionId);\n\t\t\treturn session === undefined\n\t\t\t\t? mcpError(404, -32001, \"Session not found\")\n\t\t\t\t: session.transport.handleRequest(request);\n\t\t}\n\n\t\tif (request.method !== \"POST\") {\n\t\t\treturn new Response(null, { status: 405 });\n\t\t}\n\t\tif (!(await isInitializeRequest(request))) {\n\t\t\treturn mcpError(400, -32000, \"Mcp-Session-Id header is required\");\n\t\t}\n\n\t\tconst session = await createSession();\n\t\ttry {\n\t\t\tconst response = await session.transport.handleRequest(request);\n\t\t\tif (response.status < 400) {\n\t\t\t\treturn response;\n\t\t\t}\n\t\t\tconst sessionId = session.transport.sessionId;\n\t\t\tif (sessionId !== undefined) {\n\t\t\t\tsessions.delete(sessionId);\n\t\t\t}\n\t\t\tawait session.server.close();\n\t\t\treturn response;\n\t\t} catch (error) {\n\t\t\tconst sessionId = session.transport.sessionId;\n\t\t\tif (sessionId !== undefined) {\n\t\t\t\tsessions.delete(sessionId);\n\t\t\t}\n\t\t\tawait session.server.close();\n\t\t\tthrow error;\n\t\t}\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uCAAyD;AAuBzD,SAAS,SAAS,QAAgB,MAAc,SAA2B;AAC1E,SAAO,SAAS;AAAA,IACf;AAAA,MACC,SAAS;AAAA,MACT,OAAO,EAAE,MAAM,QAAQ;AAAA,MACvB,IAAI;AAAA,IACL;AAAA,IACA,EAAE,OAAO;AAAA,EACV;AACD;AAEA,eAAe,oBAAoB,SAAoC;AACtE,MAAI,QAAQ,WAAW,QAAQ;AAC9B,WAAO;AAAA,EACR;AAEA,MAAI;AACH,UAAM,OAAgB,MAAM,QAAQ,MAAM,EAAE,KAAK;AACjD,WACC,OAAO,SAAS,YAChB,SAAS,QACT,YAAY,QACZ,KAAK,WAAW;AAAA,EAElB,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AASO,SAAS,iBACf,cACA,UAA6B,CAAC,GACjB;AACb,QAAM,WAAW,oBAAI,IAA6B;AAElD,iBAAe,gBAA0C;AACxD,UAAM,SAAS,MAAM,aAAa;AAClC,QAAI;AACJ,UAAM,YAAY,IAAI,0EAAyC;AAAA,MAC9D,oBAAoB,MAAM,OAAO,WAAW;AAAA,MAC5C,oBAAoB,QAAQ,sBAAsB;AAAA,MAClD,sBAAsB,CAAC,cAAc;AACpC,iBAAS,IAAI,WAAW,OAAO;AAAA,MAChC;AAAA,MACA,iBAAiB,OAAO,cAAc;AACrC,iBAAS,OAAO,SAAS;AACzB,cAAM,OAAO,MAAM;AAAA,MACpB;AAAA,IACD,CAAC;AACD,cAAU,EAAE,QAAQ,UAAU;AAC9B,QAAI;AACH,YAAM,OAAO,QAAQ,SAAS;AAAA,IAC/B,SAAS,OAAO;AACf,UAAI;AACH,cAAM,OAAO,MAAM;AAAA,MACpB,QAAQ;AAAA,MAER;AACA,YAAM;AAAA,IACP;AACA,WAAO;AAAA,EACR;AAEA,SAAO,OAAO,YAAY;AACzB,UAAM,YAAY,QAAQ,QAAQ,IAAI,gBAAgB;AACtD,QAAI,cAAc,MAAM;AACvB,YAAMA,WAAU,SAAS,IAAI,SAAS;AACtC,aAAOA,aAAY,SAChB,SAAS,KAAK,QAAQ,mBAAmB,IACzCA,SAAQ,UAAU,cAAc,OAAO;AAAA,IAC3C;AAEA,QAAI,QAAQ,WAAW,QAAQ;AAC9B,aAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC1C;AACA,QAAI,CAAE,MAAM,oBAAoB,OAAO,GAAI;AAC1C,aAAO,SAAS,KAAK,OAAQ,mCAAmC;AAAA,IACjE;AAEA,UAAM,UAAU,MAAM,cAAc;AACpC,QAAI;AACH,YAAM,WAAW,MAAM,QAAQ,UAAU,cAAc,OAAO;AAC9D,UAAI,SAAS,SAAS,KAAK;AAC1B,eAAO;AAAA,MACR;AACA,YAAMC,aAAY,QAAQ,UAAU;AACpC,UAAIA,eAAc,QAAW;AAC5B,iBAAS,OAAOA,UAAS;AAAA,MAC1B;AACA,YAAM,QAAQ,OAAO,MAAM;AAC3B,aAAO;AAAA,IACR,SAAS,OAAO;AACf,YAAMA,aAAY,QAAQ,UAAU;AACpC,UAAIA,eAAc,QAAW;AAC5B,iBAAS,OAAOA,UAAS;AAAA,MAC1B;AACA,YAAM,QAAQ,OAAO,MAAM;AAC3B,YAAM;AAAA,IACP;AAAA,EACD;AACD;","names":["session","sessionId"]}
|
package/dist/http.d.cts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
2
|
+
|
|
3
|
+
interface McpHttpServerConnection {
|
|
4
|
+
connect(transport: Transport): Promise<void>;
|
|
5
|
+
close(): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
type McpHandler = (request: Request) => Promise<Response>;
|
|
8
|
+
interface McpHandlerOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Returns JSON responses instead of SSE streams for request/response MCP calls.
|
|
11
|
+
* Defaults to true for a simple request/response Fetch handler.
|
|
12
|
+
*/
|
|
13
|
+
readonly enableJsonResponse?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates a Fetch-compatible Streamable HTTP handler with SDK-managed sessions.
|
|
17
|
+
*
|
|
18
|
+
* Each stateful MCP session receives its own compiled server and official Web
|
|
19
|
+
* Standard transport. The adapter routes sessions before delegating valid MCP
|
|
20
|
+
* traffic, HTTP methods, JSON-RPC framing, and lifecycle behavior to the SDK.
|
|
21
|
+
*/
|
|
22
|
+
declare function createMcpHandler<Server extends McpHttpServerConnection>(createServer: () => Server | Promise<Server>, options?: McpHandlerOptions): McpHandler;
|
|
23
|
+
|
|
24
|
+
export { type McpHandler, type McpHandlerOptions, type McpHttpServerConnection, createMcpHandler };
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
2
|
+
|
|
3
|
+
interface McpHttpServerConnection {
|
|
4
|
+
connect(transport: Transport): Promise<void>;
|
|
5
|
+
close(): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
type McpHandler = (request: Request) => Promise<Response>;
|
|
8
|
+
interface McpHandlerOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Returns JSON responses instead of SSE streams for request/response MCP calls.
|
|
11
|
+
* Defaults to true for a simple request/response Fetch handler.
|
|
12
|
+
*/
|
|
13
|
+
readonly enableJsonResponse?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Creates a Fetch-compatible Streamable HTTP handler with SDK-managed sessions.
|
|
17
|
+
*
|
|
18
|
+
* Each stateful MCP session receives its own compiled server and official Web
|
|
19
|
+
* Standard transport. The adapter routes sessions before delegating valid MCP
|
|
20
|
+
* traffic, HTTP methods, JSON-RPC framing, and lifecycle behavior to the SDK.
|
|
21
|
+
*/
|
|
22
|
+
declare function createMcpHandler<Server extends McpHttpServerConnection>(createServer: () => Server | Promise<Server>, options?: McpHandlerOptions): McpHandler;
|
|
23
|
+
|
|
24
|
+
export { type McpHandler, type McpHandlerOptions, type McpHttpServerConnection, createMcpHandler };
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// src/http.ts
|
|
2
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
3
|
+
function mcpError(status, code, message) {
|
|
4
|
+
return Response.json(
|
|
5
|
+
{
|
|
6
|
+
jsonrpc: "2.0",
|
|
7
|
+
error: { code, message },
|
|
8
|
+
id: null
|
|
9
|
+
},
|
|
10
|
+
{ status }
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
async function isInitializeRequest(request) {
|
|
14
|
+
if (request.method !== "POST") {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const body = await request.clone().json();
|
|
19
|
+
return typeof body === "object" && body !== null && "method" in body && body.method === "initialize";
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function createMcpHandler(createServer, options = {}) {
|
|
25
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
26
|
+
async function createSession() {
|
|
27
|
+
const server = await createServer();
|
|
28
|
+
let session;
|
|
29
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
30
|
+
sessionIdGenerator: () => crypto.randomUUID(),
|
|
31
|
+
enableJsonResponse: options.enableJsonResponse ?? true,
|
|
32
|
+
onsessioninitialized: (sessionId) => {
|
|
33
|
+
sessions.set(sessionId, session);
|
|
34
|
+
},
|
|
35
|
+
onsessionclosed: async (sessionId) => {
|
|
36
|
+
sessions.delete(sessionId);
|
|
37
|
+
await server.close();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
session = { server, transport };
|
|
41
|
+
try {
|
|
42
|
+
await server.connect(transport);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
try {
|
|
45
|
+
await server.close();
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
return session;
|
|
51
|
+
}
|
|
52
|
+
return async (request) => {
|
|
53
|
+
const sessionId = request.headers.get("mcp-session-id");
|
|
54
|
+
if (sessionId !== null) {
|
|
55
|
+
const session2 = sessions.get(sessionId);
|
|
56
|
+
return session2 === void 0 ? mcpError(404, -32001, "Session not found") : session2.transport.handleRequest(request);
|
|
57
|
+
}
|
|
58
|
+
if (request.method !== "POST") {
|
|
59
|
+
return new Response(null, { status: 405 });
|
|
60
|
+
}
|
|
61
|
+
if (!await isInitializeRequest(request)) {
|
|
62
|
+
return mcpError(400, -32e3, "Mcp-Session-Id header is required");
|
|
63
|
+
}
|
|
64
|
+
const session = await createSession();
|
|
65
|
+
try {
|
|
66
|
+
const response = await session.transport.handleRequest(request);
|
|
67
|
+
if (response.status < 400) {
|
|
68
|
+
return response;
|
|
69
|
+
}
|
|
70
|
+
const sessionId2 = session.transport.sessionId;
|
|
71
|
+
if (sessionId2 !== void 0) {
|
|
72
|
+
sessions.delete(sessionId2);
|
|
73
|
+
}
|
|
74
|
+
await session.server.close();
|
|
75
|
+
return response;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
const sessionId2 = session.transport.sessionId;
|
|
78
|
+
if (sessionId2 !== void 0) {
|
|
79
|
+
sessions.delete(sessionId2);
|
|
80
|
+
}
|
|
81
|
+
await session.server.close();
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
createMcpHandler
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/http.ts"],"sourcesContent":["import { WebStandardStreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\n\nexport interface McpHttpServerConnection {\n\tconnect(transport: Transport): Promise<void>;\n\tclose(): Promise<void>;\n}\n\nexport type McpHandler = (request: Request) => Promise<Response>;\n\nexport interface McpHandlerOptions {\n\t/**\n\t * Returns JSON responses instead of SSE streams for request/response MCP calls.\n\t * Defaults to true for a simple request/response Fetch handler.\n\t */\n\treadonly enableJsonResponse?: boolean;\n}\n\ninterface Session<Server extends McpHttpServerConnection> {\n\treadonly server: Server;\n\treadonly transport: WebStandardStreamableHTTPServerTransport;\n}\n\nfunction mcpError(status: number, code: number, message: string): Response {\n\treturn Response.json(\n\t\t{\n\t\t\tjsonrpc: \"2.0\",\n\t\t\terror: { code, message },\n\t\t\tid: null,\n\t\t},\n\t\t{ status },\n\t);\n}\n\nasync function isInitializeRequest(request: Request): Promise<boolean> {\n\tif (request.method !== \"POST\") {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\tconst body: unknown = await request.clone().json();\n\t\treturn (\n\t\t\ttypeof body === \"object\" &&\n\t\t\tbody !== null &&\n\t\t\t\"method\" in body &&\n\t\t\tbody.method === \"initialize\"\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Creates a Fetch-compatible Streamable HTTP handler with SDK-managed sessions.\n *\n * Each stateful MCP session receives its own compiled server and official Web\n * Standard transport. The adapter routes sessions before delegating valid MCP\n * traffic, HTTP methods, JSON-RPC framing, and lifecycle behavior to the SDK.\n */\nexport function createMcpHandler<Server extends McpHttpServerConnection>(\n\tcreateServer: () => Server | Promise<Server>,\n\toptions: McpHandlerOptions = {},\n): McpHandler {\n\tconst sessions = new Map<string, Session<Server>>();\n\n\tasync function createSession(): Promise<Session<Server>> {\n\t\tconst server = await createServer();\n\t\tlet session: Session<Server>;\n\t\tconst transport = new WebStandardStreamableHTTPServerTransport({\n\t\t\tsessionIdGenerator: () => crypto.randomUUID(),\n\t\t\tenableJsonResponse: options.enableJsonResponse ?? true,\n\t\t\tonsessioninitialized: (sessionId) => {\n\t\t\t\tsessions.set(sessionId, session);\n\t\t\t},\n\t\t\tonsessionclosed: async (sessionId) => {\n\t\t\t\tsessions.delete(sessionId);\n\t\t\t\tawait server.close();\n\t\t\t},\n\t\t});\n\t\tsession = { server, transport };\n\t\ttry {\n\t\t\tawait server.connect(transport);\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tawait server.close();\n\t\t\t} catch {\n\t\t\t\t// Preserve the original transport connection failure.\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\treturn session;\n\t}\n\n\treturn async (request) => {\n\t\tconst sessionId = request.headers.get(\"mcp-session-id\");\n\t\tif (sessionId !== null) {\n\t\t\tconst session = sessions.get(sessionId);\n\t\t\treturn session === undefined\n\t\t\t\t? mcpError(404, -32001, \"Session not found\")\n\t\t\t\t: session.transport.handleRequest(request);\n\t\t}\n\n\t\tif (request.method !== \"POST\") {\n\t\t\treturn new Response(null, { status: 405 });\n\t\t}\n\t\tif (!(await isInitializeRequest(request))) {\n\t\t\treturn mcpError(400, -32000, \"Mcp-Session-Id header is required\");\n\t\t}\n\n\t\tconst session = await createSession();\n\t\ttry {\n\t\t\tconst response = await session.transport.handleRequest(request);\n\t\t\tif (response.status < 400) {\n\t\t\t\treturn response;\n\t\t\t}\n\t\t\tconst sessionId = session.transport.sessionId;\n\t\t\tif (sessionId !== undefined) {\n\t\t\t\tsessions.delete(sessionId);\n\t\t\t}\n\t\t\tawait session.server.close();\n\t\t\treturn response;\n\t\t} catch (error) {\n\t\t\tconst sessionId = session.transport.sessionId;\n\t\t\tif (sessionId !== undefined) {\n\t\t\t\tsessions.delete(sessionId);\n\t\t\t}\n\t\t\tawait session.server.close();\n\t\t\tthrow error;\n\t\t}\n\t};\n}\n"],"mappings":";AAAA,SAAS,gDAAgD;AAuBzD,SAAS,SAAS,QAAgB,MAAc,SAA2B;AAC1E,SAAO,SAAS;AAAA,IACf;AAAA,MACC,SAAS;AAAA,MACT,OAAO,EAAE,MAAM,QAAQ;AAAA,MACvB,IAAI;AAAA,IACL;AAAA,IACA,EAAE,OAAO;AAAA,EACV;AACD;AAEA,eAAe,oBAAoB,SAAoC;AACtE,MAAI,QAAQ,WAAW,QAAQ;AAC9B,WAAO;AAAA,EACR;AAEA,MAAI;AACH,UAAM,OAAgB,MAAM,QAAQ,MAAM,EAAE,KAAK;AACjD,WACC,OAAO,SAAS,YAChB,SAAS,QACT,YAAY,QACZ,KAAK,WAAW;AAAA,EAElB,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AASO,SAAS,iBACf,cACA,UAA6B,CAAC,GACjB;AACb,QAAM,WAAW,oBAAI,IAA6B;AAElD,iBAAe,gBAA0C;AACxD,UAAM,SAAS,MAAM,aAAa;AAClC,QAAI;AACJ,UAAM,YAAY,IAAI,yCAAyC;AAAA,MAC9D,oBAAoB,MAAM,OAAO,WAAW;AAAA,MAC5C,oBAAoB,QAAQ,sBAAsB;AAAA,MAClD,sBAAsB,CAAC,cAAc;AACpC,iBAAS,IAAI,WAAW,OAAO;AAAA,MAChC;AAAA,MACA,iBAAiB,OAAO,cAAc;AACrC,iBAAS,OAAO,SAAS;AACzB,cAAM,OAAO,MAAM;AAAA,MACpB;AAAA,IACD,CAAC;AACD,cAAU,EAAE,QAAQ,UAAU;AAC9B,QAAI;AACH,YAAM,OAAO,QAAQ,SAAS;AAAA,IAC/B,SAAS,OAAO;AACf,UAAI;AACH,cAAM,OAAO,MAAM;AAAA,MACpB,QAAQ;AAAA,MAER;AACA,YAAM;AAAA,IACP;AACA,WAAO;AAAA,EACR;AAEA,SAAO,OAAO,YAAY;AACzB,UAAM,YAAY,QAAQ,QAAQ,IAAI,gBAAgB;AACtD,QAAI,cAAc,MAAM;AACvB,YAAMA,WAAU,SAAS,IAAI,SAAS;AACtC,aAAOA,aAAY,SAChB,SAAS,KAAK,QAAQ,mBAAmB,IACzCA,SAAQ,UAAU,cAAc,OAAO;AAAA,IAC3C;AAEA,QAAI,QAAQ,WAAW,QAAQ;AAC9B,aAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC1C;AACA,QAAI,CAAE,MAAM,oBAAoB,OAAO,GAAI;AAC1C,aAAO,SAAS,KAAK,OAAQ,mCAAmC;AAAA,IACjE;AAEA,UAAM,UAAU,MAAM,cAAc;AACpC,QAAI;AACH,YAAM,WAAW,MAAM,QAAQ,UAAU,cAAc,OAAO;AAC9D,UAAI,SAAS,SAAS,KAAK;AAC1B,eAAO;AAAA,MACR;AACA,YAAMC,aAAY,QAAQ,UAAU;AACpC,UAAIA,eAAc,QAAW;AAC5B,iBAAS,OAAOA,UAAS;AAAA,MAC1B;AACA,YAAM,QAAQ,OAAO,MAAM;AAC3B,aAAO;AAAA,IACR,SAAS,OAAO;AACf,YAAMA,aAAY,QAAQ,UAAU;AACpC,UAAIA,eAAc,QAAW;AAC5B,iBAAS,OAAOA,UAAS;AAAA,MAC1B;AACA,YAAM,QAAQ,OAAO,MAAM;AAC3B,YAAM;AAAA,IACP;AAAA,EACD;AACD;","names":["session","sessionId"]}
|