Skip to content

feat: add Streamable HTTP transport support#326

Open
keykbd wants to merge 1 commit into
kintone:mainfrom
keykbd:main
Open

feat: add Streamable HTTP transport support#326
keykbd wants to merge 1 commit into
kintone:mainfrom
keykbd:main

Conversation

@keykbd

@keykbd keykbd commented Mar 4, 2026

Copy link
Copy Markdown

Why

Addresses the HTTP transport portion of #304.

Currently the server only supports stdio transport, which requires a 1:1 process
per client. Adding Streamable HTTP transport enables deployment on remote
environments such as AWS AgentCore, or running as a shared MCP server for a team.

What

  • Add --transport http to start in Streamable HTTP mode (default remains stdio)
  • No new runtime dependencies (uses SDK built-in StreamableHTTPServerTransport + node:http)
  • Stateless mode (each request creates an independent session)
  • Tests added (14 for HTTP server, 9 for config parsing)

Out of scope (planned for a follow-up PR):

How to test

pnpm build && pnpm test && pnpm lint

To manually verify HTTP mode:

KINTONE_BASE_URL=https://example.cybozu.com \
KINTONE_USERNAME=user KINTONE_PASSWORD=pass \
node dist/index.js --transport http
# -> HTTP server listening on http://127.0.0.1:3000/mcp

Checklist

  • Updated documentation if it is required.
  • Added tests if it is required.
  • Passed pnpm lint and pnpm test on the root directory.

cc @nameless-mc @shabaraba @neos-nozaki

Add --transport http option to start in Streamable HTTP mode
(default remains stdio). Uses SDK built-in StreamableHTTPServerTransport
+ node:http with no new runtime dependencies.

- Stateless mode (each request creates an independent session)
- Origin header validation, Content-Type check, 1MB body limit
- Default 127.0.0.1 bind, --port and --hostname options
- Graceful shutdown with SIGTERM/SIGINT handling
- Tests added (14 for HTTP server, 9 for config parsing)
@keykbd keykbd requested a review from a team as a code owner March 4, 2026 03:47
@keykbd keykbd requested review from chihiro-adachi and nameless-mc and removed request for a team March 4, 2026 03:47
@shabaraba

Copy link
Copy Markdown
Member

@keykbd
Thank you for creating the PR. We'll take a look later!

@shabaraba shabaraba left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@keykbd
Thank you for this PR. We apologize for the very delayed review 🙇

We've gone through the changes and left some review comments. Please take a look when you get a chance.

Thank you!

Comment thread src/transport/http.ts
// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {};

const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove localhost from the set. Unlike the IP literals 127.0.0.1 and ::1, it is a DNS-resolvable hostname and may raise security concerns.

Comment thread src/transport/http.ts
Comment on lines +131 to +134
const cleanup = () => {
transport?.close().catch(noop);
server?.close().catch(noop);
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since both res.on("close", cleanup) and the explicit cleanup() call in the catch block may invoke transport.close() / server.close() twice on error, please make cleanup idempotent.

Suggested change
const cleanup = () => {
transport?.close().catch(noop);
server?.close().catch(noop);
};
let cleaned = false;
const cleanup = () => {
if (cleaned) return;
cleaned = true;
transport?.close().catch(noop);
server?.close().catch(noop);
};

Comment thread README.md

kintoneの公式ローカルMCPサーバーです。

<!-- NOTE: TOCはpnpm doc:update-tocで自動生成されます。 -->

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new section has been added, so please run pnpm doc:update-toc.

Comment on lines +175 to +178
if (!connectionReset) {
// If we got a response, it must have been 413
expect(true).toBe(true);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion can be safely removed.

expect(mockHandleRequest).not.toHaveBeenCalled();
});

it("should return 500 and cleanup when connect throws", async () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add a test case verifying that cleanup is called in the normal (non-error) flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants