Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,30 @@ Then configure your MCP client:
}
```

### 🌊 Streamable HTTP Transport

For Streamable HTTP transport, first start the server:

```bash
npx @kontent-ai/mcp-server@latest shttp
```

With environment variables in a `.env` file, or otherwise accessible to the process:
```env
KONTENT_API_KEY=<management-api-key>
KONTENT_ENVIRONMENT_ID=<environment-id>
PORT=3001 # optional, defaults to 3001
```

Then configure your MCP client:
```json
{
"kontent-ai-http": {
"url": "http://localhost:3001/mcp"
}
}
```

## 💻 Development

### 🛠 Local Installation
Expand All @@ -177,10 +201,12 @@ npm run build
# Start the server
npm run start:sse # For SSE transport
npm run start:stdio # For STDIO transport
npm run start:shttp # For Streamable HTTP transport

# Start the server with automatic reloading (no need to build first)
npm run dev:sse # For SSE transport
npm run dev:stdio # For STDIO transport
npm run dev:shttp # For Streamable HTTP transport
```

### 📂 Project Structure
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
"build": "rimraf build && tsc",
"start:stdio": "node build/bin.js stdio",
"start:sse": "node build/bin.js sse",
"start:shttp": "node build/bin.js shttp",
"dev:stdio": "tsx watch src/bin.ts stdio",
"dev:sse": "tsx watch src/bin.ts sse",
"dev:shttp": "tsx watch src/bin.ts shttp",
"format": "cross-env node node_modules/@biomejs/biome/bin/biome ci ./ --config-path=./biome.json",
"format:fix": "cross-env node node_modules/@biomejs/biome/bin/biome check ./ --fix --unsafe --config-path=./biome.json"
},
Expand Down
81 changes: 78 additions & 3 deletions src/bin.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,82 @@
#!/usr/bin/env node
import "dotenv/config";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import "dotenv/config";
import express from "express";
import packageJson from "../package.json" with { type: "json" };
import { createServer } from "./server.js";

const version = packageJson.version;

async function startStreamableHTTP() {
const app = express();
app.use(express.json());

app.post("/mcp", async (req, res) => {
try {
const { server } = createServer();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
res.on("close", () => {
console.log("Request closed");
transport.close();
server.close();
});
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
} catch (error) {
console.error("Error handling MCP request:", error);
if (!res.headersSent) {
res.status(500).json({
jsonrpc: "2.0",
error: {
code: -32603,
message: "Internal server error",
},
id: null,
});
}
}
});

app.get("/mcp", async (_, res) => {
console.log("Received GET MCP request");
res.writeHead(405).end(
JSON.stringify({
jsonrpc: "2.0",
error: {
code: -32000,
message: "Method not allowed.",
},
id: null,
}),
);
});

app.delete("/mcp", async (_, res) => {
console.log("Received DELETE MCP request");
res.writeHead(405).end(
JSON.stringify({
jsonrpc: "2.0",
error: {
code: -32000,
message: "Method not allowed.",
},
id: null,
}),
);
});

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(
`Kontent.ai MCP Server v${version} (Streamable HTTP) running on port ${PORT}`,
);
});
}

async function startSSE() {
const app = express();
const { server } = createServer();
Expand Down Expand Up @@ -44,16 +113,22 @@ async function main() {

if (
!transportType ||
(transportType !== "stdio" && transportType !== "sse")
(transportType !== "stdio" &&
transportType !== "sse" &&
transportType !== "shttp")
) {
console.error("Please specify a valid transport type: stdio or sse");
console.error(
"Please specify a valid transport type: stdio, sse, or shttp",
);
process.exit(1);
}

if (transportType === "stdio") {
await startStdio();
} else if (transportType === "sse") {
await startSSE();
} else if (transportType === "shttp") {
await startStreamableHTTP();
}
}

Expand Down