You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Proposal: native MCP protocol primitives in the standard library #7185
MCP (Model Context Protocol) is becoming a standard for AI tool integration. Deno could be the most natural platform for building MCP servers by adding native protocol primitives to the standard library. Currently MCP server authors using Deno must either use the npm package @modelcontextprotocol/sdk (Node.js-focused) or implement JSON-RPC manually over std/http. A Deno-native std/mcp module would provide proper TypeScript types for the MCP protocol, handle JSON-RPC over stdin/stdout and HTTP transports, integrate with Deno's permission model, and require zero configuration. MCP servers are often single-file scripts which maps perfectly to Deno's design. Deno's security model (--allow-read, --allow-net) also maps cleanly to MCP's tool permission concept.
I've just set up my fork to start mapping out the baseline directory structure and core JSON-RPC types. Since you're an active contributor here, I'm completely open to how we tackle this—whether you'd prefer to take the lead, or if we want to team up on a draft PR to align on Deno's stream model for the Stdio/SSE transports.
Let me know what works best for you and your availability!
I'd be happy to take the lead and highlight a plan in here so we can discuss strenghts and flaws of it, what do you think? I can probably set up the plan by later today and paste it here as a markdown doc so we can talk about it
That sounds like a perfect plan, looking forward to reviewing the markdown breakdown.
Once we align on the architecture and core design, I’m more than happy to split the implementation workload with you whether that's mapping out the schema types or building out the initial transport streams.
Take your time, and see you back here later today!
So I have made the plan in an RFC format and will paste it in the next comment so you can let me know what you think @fy2ne
I've thought about the split of work and if it's not a big deal, I'd like to take the big chunk in the bootstrap and the server part and you could do the client one, but I'm also open to suggestions once you see the plan, not trying to push you off, just got a little bit of free time and it's been long since I've done my last big contribution lolz
The design targets zero configuration and native integration with Deno's
permission model, making it straightforward to ship MCP servers as single-file
Deno scripts.
Motivation
MCP has become a common way to connect LLM applications with external tools,
resources, and prompts. In practice, many MCP servers are small, single-purpose
programs — often a single script that exposes one or a few tools. This maps
directly onto Deno's strengths:
MCP server reality
Deno capability
Single-file scripts
deno run server.ts with no build step
TypeScript-first ecosystem
Native TypeScript, no transpiler config
Sandboxed tool execution
Explicit --allow-* permission flags
Stdio transport for local hosts
First-class Deno.stdin / Deno.stdout
HTTP transport for remote hosts
@std/http already in std
Today, MCP SDKs exist primarily for Node.js and Python. Deno users must either
reach for npm compatibility layers or reimplement protocol details by hand. A
first-party std package would give Deno a canonical, well-tested foundation for
building MCP servers and clients.
The value proposition in one line:
Write an MCP server in a single TypeScript file and run it with deno run --allow-read server.ts — no package.json, no bundler, no config
files.
Why this fits deno_std
Composes existing packages
The MCP wire format is JSON-RPC 2.0 over newline-delimited JSON (stdio) or
Streamable HTTP. These primitives are already well-served by existing std
packages:
@std/mcp should compose these packages rather than reinvent them.
Precedent in std
Std already ships protocol and serialization packages:
@std/json, @std/jsonc, @std/msgpack — data formats
@std/http, @std/net — network protocols
MCP is an application protocol built on JSON-RPC. A dedicated package follows
the same pattern as @std/http (HTTP primitives) and @std/msgpack
(MessagePack encode/decode).
This is the primary differentiator from Node-based MCP SDKs.
Deno's --allow-* flags are the server's security boundary. An MCP tool that
reads the filesystem should require --allow-read. A tool that calls an
external API should require --allow-net. The runtime enforces this at the OS
level — not at the application layer.
The proposal includes an optional permission helper (either in core or a
sub-path — see Open questions):
import{McpServer,requirePermissions,StdioTransport}from"@std/mcp";constserver=newMcpServer({name: "fs-tools",version: "1.0.0"});server.tool({name: "read_file",description: "Read a file from disk",permissions: ["read"],// declarative hintinputSchema: {type: "object",properties: {path: {type: "string"}},required: ["path"],},handler: async({ path })=>{awaitrequirePermissions({read: true});consttext=awaitDeno.readTextFile(path);return{content: [{type: "text", text }]};},});
requirePermissions() would use Deno.permissions.query() and Deno.permissions.request() to check or prompt for the needed capabilities
before the handler runs. This stays additive and optional so the type layer
remains runtime-neutral and browser-compatible.
Architecture
flowchart LR
subgraph shared [Shared primitives]
types[types.ts JSON-RPC and MCP types]
rpc[json_rpc.ts framing and dispatch]
end
subgraph transports [Transports]
stdio[StdioTransport stdin and stdout]
http[HttpTransport Streamable HTTP]
end
server[McpServer tool resource prompt] --> rpc
client[McpClient callTool readResource] --> rpc
rpc --> types
server --> transports
client --> transports
stdio -->|composes| stdlib1[std json streams io]
http -->|composes| stdlib2[std http]
Loading
Message flow (stdio server)
sequenceDiagram
participant Host as MCP Host
participant Stdio as StdioTransport
participant Rpc as JsonRpcPeer
participant Srv as McpServer
Host->>Stdio: JSON-RPC initialize request
Stdio->>Rpc: parse newline-delimited JSON
Rpc->>Srv: dispatch initialize
Srv->>Rpc: initialize result
Rpc->>Stdio: serialize response
Stdio->>Host: JSON-RPC response
Host->>Stdio: tools/call request
Stdio->>Rpc: parse message
Rpc->>Srv: dispatch tools/call
Srv->>Srv: run tool handler
Srv->>Rpc: CallToolResult
Rpc->>Stdio: serialize response
Stdio->>Host: JSON-RPC response
Loading
API sketches
All public APIs ship as unstable (@experimental + UNSTABLE warning)
until vetted by the community and core team.
importtype{JsonRpcError,JsonRpcMessage,JsonRpcRequest}from"./types.ts";/** Standard JSON-RPC error codes. */exportconstPARSE_ERROR=-32700;exportconstINVALID_REQUEST=-32600;exportconstMETHOD_NOT_FOUND=-32601;exportconstINVALID_PARAMS=-32602;exportconstINTERNAL_ERROR=-32603;/** Create a JSON-RPC request. */exportfunctioncreateRequest(id: string|number,method: string,params?: Record<string,unknown>,): JsonRpcRequest;/** Create a JSON-RPC notification. */exportfunctioncreateNotification(method: string,params?: Record<string,unknown>,): JsonRpcNotification;/** Create a JSON-RPC error response. */exportfunctioncreateErrorResponse(id: string|number|null,error: JsonRpcError,): JsonRpcErrorResponse;/** Parse a single JSON-RPC message from a JSON value. */exportfunctionparseMessage(value: unknown): JsonRpcMessage;/** Transport-agnostic peer for request/response correlation. */exportinterfaceJsonRpcPeer{/** Register a handler for incoming requests/notifications. */onRequest(method: string,handler: RequestHandler): void;/** Send a request and await the correlated response. */request<T>(method: string,params?: Record<string,unknown>): Promise<T>;/** Send a notification (no response expected). */notify(method: string,params?: Record<string,unknown>): void;/** Process an incoming raw message. */handleMessage(message: JsonRpcMessage): Promise<JsonRpcMessage|undefined>;}exportfunctioncreateJsonRpcPeer(): JsonRpcPeer;
McpTransport interface
importtype{JsonRpcMessage}from"./types.ts";/** Common interface for MCP transports. */exportinterfaceMcpTransport{/** Start the transport and begin reading messages. */start(): Promise<void>;/** Send a JSON-RPC message to the peer. */send(message: JsonRpcMessage): Promise<void>;/** Register a callback for incoming messages. */onMessage(handler: (message: JsonRpcMessage)=>void): void;/** Close the transport gracefully. */close(): Promise<void>;}
stdio_transport.ts
importtype{McpTransport}from"./transport.ts";exportinterfaceStdioTransportOptions{/** Readable stream for incoming messages. @default Deno.stdin */input?: ReadableStream<Uint8Array>;/** Writable stream for outgoing messages. @default Deno.stdout */output?: WritableStream<Uint8Array>;}/** Stdio transport using newline-delimited JSON. */exportclassStdioTransportimplementsMcpTransport{constructor(options?: StdioTransportOptions);start(): Promise<void>;send(message: JsonRpcMessage): Promise<void>;onMessage(handler: (message: JsonRpcMessage)=>void): void;close(): Promise<void>;}
Granular exports mirror json/deno.json and msgpack/deno.json, allowing users to import only what
they need (e.g. @std/mcp/types for types-only usage in browser code).
McpServer handles initialize and returns server capabilities
McpClient callTool() sends tools/call and returns result
HTTP transport tests can use @std/http test utilities. Stdio transport tests
can pipe ReadableStream / WritableStream pairs without touching real
stdin/stdout.
Spec targeting
Initial target: 2025-11-25 (stable)
The first implementation targets the current stable MCP specification:
McpServer / McpClient lifecycle changes behind transport version flag
Removes Mcp-Session-Id header
HttpTransport session management simplified
Adds required Mcp-Method / Mcp-Name headers
HttpTransport header injection
Protocol version in _meta
Types gain _meta fields; version negotiation moves to per-request
Removes roots, sampling, logging capabilities
Types marked deprecated; no SDK support for removed features
Adds server/discover
New client method
The layered design keeps version-specific behavior behind McpTransport and
protocol version options, so the type layer remains stable while transports
adapt:
Authentication and authorization — transport security is the deployer's
responsibility; @std/http provides the HTTP primitives
Deprecated transports — no HTTP+SSE from 2024-11-05
MCP registry / discovery service — out of scope for a primitives package
Code generation from JSON Schema — types are hand-maintained to mirror schema.ts; automation can be added later
Repo-wide touchpoints
Adding a new package requires updates across the monorepo. These should land in
follow-up PRs after this proposal is accepted.
Preceding PR (required first)
Per CONTRIBUTING.md, add mcp(/unstable)? to the scopes list in .github/workflows/title.yml before the implementation PR. This enables semantic PR titles like feat(mcp): add protocol types.
May need update if it validates workspace membership
Validation
All changes must pass:
deno task ok
This runs lint, format check, browser compat check, and the full test suite.
Implementation phases
Phase 1: Types + JSON-RPC (foundation)
types.ts with full MCP type coverage
json_rpc.ts with message builders, parser, and JsonRpcPeer
Unit tests with no I/O
Phase 2: Stdio transport
stdio_transport.ts composing @std/json streams
Integration test with piped streams
Example: minimal stdio server script
Phase 3: Server API
server.ts with McpServer, tool/resource/prompt registration
Auto-handle initialize, tools/list, tools/call, etc.
End-to-end stdio server test
Phase 4: HTTP transport
http_transport.ts for Streamable HTTP (server + client)
SSE response handling
Integration test with @std/http test server
Phase 5: Client API
client.ts with McpClient
Connect over stdio and HTTP
End-to-end client/server test
Phase 6: Permissions (optional)
permissions.ts with requirePermissions() helper
Declarative permissions field on tool definitions
Documentation and examples
Open questions
1. Scope within std
Does the Deno core team consider an application-protocol package in-scope for
std? Std has historically been conservative — it provides primitives (http, json, streams) rather than application frameworks. MCP is arguably closer to @std/http (protocol helpers) than to a web framework, but it is still an
application-layer protocol tied to a specific ecosystem (LLM tooling).
Alternative: ship as @denoland/mcp outside std, with std packages as
dependencies. This reduces maintenance burden on the core team but loses the
"official Deno MCP SDK" positioning.
2. Permission integration location
Should permissions.ts ship in core @std/mcp or as a separate export path
(@std/mcp/permissions)?
Sub-path keeps @std/mcp/types and @std/mcp/json-rpc runtime-neutral
and browser-compatible
Core export makes the Deno differentiator more discoverable
3. Server + client packaging
Should server and client APIs live in one package (@std/mcp) or split?
One package: simpler discovery, shared types, matches @std/http
(server + client in one module)
Split (@std/mcp-server, @std/mcp-client): smaller dependency footprint
for server-only users
Recommendation: one package with granular exports (as proposed), since most
users will only import @std/mcp/server or @std/mcp/client.
4. Type maintenance strategy
Hand-maintained types vs. generated from the official schema.ts?
Hand-maintained: easier to review in PRs, matches std conventions, but
requires manual sync on spec updates
Generated: always in sync, but adds a code-generation step to CI
types.ts and json_rpc.ts can be browser-compatible (no Deno APIs).
Transports and server/client require Deno. Should browser compat be tested via deno task test:browser?
Recommendation: mark types.ts and json_rpc.ts as browser compatible;
transports and high-level APIs are Deno-only.
Alternatives considered
Use the official TypeScript SDK via npm
Deno supports npm packages, but:
Adds npm dependency management overhead
No Deno permission integration
Heavier dependency tree than composing std packages
Not idiomatic for single-file Deno scripts
Ship only types (no server/client)
A types-only package is useful but misses the "zero config single-file server"
value proposition. The layered design allows types-only usage via @std/mcp/types regardless.
Ship only server (no client)
Many MCP use cases are server-only (exposing tools to an LLM host). However,
client support enables testing, scripting, and MCP-to-MCP proxy scenarios with
minimal extra code since client and server share transports and JSON-RPC core.
Really well thought out RFC, the layered architecture is clean and the phase order makes sense. Happy to take the client side as you suggested.
A few things worth talking through before we start building:
2026-07-28 compat - The protocolVersion flag on HttpTransport handles the header changes fine, but the RC removes the initialize handshake entirely which means capability negotiation shifts out of McpServer lifecycle into per-request _meta. That bleeds into server and client dispatch, not just transport. Worth agreeing on the shape now before Phase 3 locks things in. Single McpServer branching internally on version, or versioned factories?
Streaming tool results not mentioned but the 2025-11-25 spec allows progressive streaming over SSE on HTTP. Intentional defer or just out of scope for now? Good to have a clear line on it early.
Std scope honestly the biggest unknown before we invest too much. Worth getting a core team member to drop a signal in this thread so we're not building toward a "not in std" wall.
On the workflow side, I'm thinking we just keep separate forks and each open our own PR to deno_std when ready, cleaner for the maintainers to review that way. No need to share access to anything. We can co-author commits when we overlap on something though, what email do you use for commits so I have it ready?
Starting the McpClient skeleton on my end now so we have something concrete to look at.
On the open questions, I lean toward one package with granular exports as you proposed, and I'd hold off on the @denoland/mcp alternative until we get a signal from the core team. No strong opinion on permissions sub-path vs core, whatever the maintainers prefer.
Hey @bartlomieju
before we get into implementation, would love a quick read on whether MCP belongs in std or as a separate @denoland/mcp package. Happy to move forward either way.
On the compat side, I lean towards a versioned factory, that way a consumer has to be explicit on which version he wants to use and we avoid confusions
The streaming tool results were a intentional defer on my end, hadn't dug deep enough to include it in the plan for now, happy to leave it to you to plan and do a follow-up PR as an additional feature for the package after we're done
I do think the std scope would be very nice though, because the prtoocol is actually becoming as core and standardized as HTTP and other ones, but I agree that maintainers should definitely get a say in this.
Yep, agreed on the workflow, we might just have to coordinate a little bit with the boostrapping and stuff (since you started the skeleton I can start when that's merged in). My commit email for co-authoring is lucashenriqueblemos@gmail.com
Versioned factories makes sense, cleaner API, I'll update the client skeleton to reflect that.
I'll plan out the streaming follow-up PR once the main package lands, happy to own it
On bootstrapping : i have the client skeleton ready locally but I want to wait for @bartlomieju to weigh in on the std scope question before I push anything.
Once we get that signal I'll open the skeleton PR first so you can align your foundation files against it. Shouldn't be long.
I create a lot of MCP servers, so I'm very interested in what would make sense here.
However, I’m concerned about drift here. The official TypeScript SDK supports Deno and covers much of this proposal, including transports and client/server APIs. A second implementation would have to track protocol changes (that tend to be big...). What concrete Deno gap would @std/mcp solve that we couldn’t address upstream?
Activity
luk3skyw4lker commentedon Jul 2, 2026
I will look into this if nobody else is into it right now
fy2ne commentedon Jul 2, 2026
Hey @luk3skyw4lker, thanks for reaching out!
I've just set up my fork to start mapping out the baseline directory structure and core JSON-RPC types. Since you're an active contributor here, I'm completely open to how we tackle this—whether you'd prefer to take the lead, or if we want to team up on a draft PR to align on Deno's stream model for the Stdio/SSE transports.
Let me know what works best for you and your availability!
luk3skyw4lker commentedon Jul 2, 2026
Happy to hear that @fy2ne!
I'd be happy to take the lead and highlight a plan in here so we can discuss strenghts and flaws of it, what do you think? I can probably set up the plan by later today and paste it here as a markdown doc so we can talk about it
fy2ne commentedon Jul 2, 2026
That sounds like a perfect plan, looking forward to reviewing the markdown breakdown.
Once we align on the architecture and core design, I’m more than happy to split the implementation workload with you whether that's mapping out the schema types or building out the initial transport streams.
Take your time, and see you back here later today!
luk3skyw4lker commentedon Jul 3, 2026
So I have made the plan in an RFC format and will paste it in the next comment so you can let me know what you think @fy2ne
I've thought about the split of work and if it's not a big deal, I'd like to take the big chunk in the bootstrap and the server part and you could do the client one, but I'm also open to suggestions once you see the plan, not trying to push you off, just got a little bit of free time and it's been long since I've done my last big contribution lolz
luk3skyw4lker commentedon Jul 3, 2026
RFC:
@std/mcp— MCP primitives for the Deno Standard LibrarySummary
This proposal introduces a new
@std/mcppackage to the Deno Standard Library.The package provides:
Model Context Protocol (MCP) specification
The design targets zero configuration and native integration with Deno's
permission model, making it straightforward to ship MCP servers as single-file
Deno scripts.
Motivation
MCP has become a common way to connect LLM applications with external tools,
resources, and prompts. In practice, many MCP servers are small, single-purpose
programs — often a single script that exposes one or a few tools. This maps
directly onto Deno's strengths:
deno run server.tswith no build step--allow-*permission flagsDeno.stdin/Deno.stdout@std/httpalready in stdToday, MCP SDKs exist primarily for Node.js and Python. Deno users must either
reach for npm compatibility layers or reimplement protocol details by hand. A
first-party std package would give Deno a canonical, well-tested foundation for
building MCP servers and clients.
The value proposition in one line:
Why this fits
deno_stdComposes existing packages
The MCP wire format is JSON-RPC 2.0 over newline-delimited JSON (stdio) or
Streamable HTTP. These primitives are already well-served by existing std
packages:
@std/json—JsonStringifyStream,JsonParseStream,JsonValue@std/streams— stream utilities@std/io— buffered I/O@std/http— HTTP server and client primitives@std/mcpshould compose these packages rather than reinvent them.Precedent in std
Std already ships protocol and serialization packages:
@std/json,@std/jsonc,@std/msgpack— data formats@std/http,@std/net— network protocolsMCP is an application protocol built on JSON-RPC. A dedicated package follows
the same pattern as
@std/http(HTTP primitives) and@std/msgpack(MessagePack encode/decode).
Architecture alignment
Per the architecture guide:
mcp/)mod.tsas the entry point with module documentationjson_rpc.ts,stdio_transport.ts)_test.tsper source fileexportsmap indeno.jsonProposed scope
The package is organized in layers. Each layer is independently useful and
testable.
1. Protocol types (
types.ts)Full TypeScript types for:
initialize,initialized)tools/list,tools/call)resources/list,resources/read,resources/templates/list)prompts/list,prompts/get)notifications/tools/list_changed, etc.)Types mirror the official MCP
schema.ts. This module is browser-compatibleand has no runtime dependencies.
2. JSON-RPC core (
json_rpc.ts)Transport-agnostic framing and dispatch helpers:
id-32700parse error,-32600invalid request, etc.)JsonRpcPeerfor sending requests and dispatching incomingmessages to handlers
3. Transports
A common
McpTransportinterface with two implementations:StdioTransport(stdio_transport.ts)Deno.stdin/Deno.stdoutstderr(stdout is reserved for protocol perspec)
@std/jsonparse/stringify streams and@std/streamsHttpTransport(http_transport.ts)/mcpendpoint@std/httpapplication/json(single response) andtext/event-stream(SSEstream) response modes
MCP-Protocol-Version,Accept, etc.)4. Server (
server.ts)High-level
McpServerAPI:initializeand capability negotiationconnect(transport)to start serving5. Client (
client.ts)High-level
McpClientAPI:listTools(),callTool(),listResources(),readResource(),listPrompts(),getPrompt()Zero configuration
The intended developer experience for a minimal MCP server:
Run with:
No
package.json, notsconfig.json, no bundler, no transport configurationbeyond choosing
StdioTransportorHttpTransport.For HTTP:
Deno permission model integration
This is the primary differentiator from Node-based MCP SDKs.
Deno's
--allow-*flags are the server's security boundary. An MCP tool thatreads the filesystem should require
--allow-read. A tool that calls anexternal API should require
--allow-net. The runtime enforces this at the OSlevel — not at the application layer.
The proposal includes an optional permission helper (either in core or a
sub-path — see Open questions):
requirePermissions()would useDeno.permissions.query()andDeno.permissions.request()to check or prompt for the needed capabilitiesbefore the handler runs. This stays additive and optional so the type layer
remains runtime-neutral and browser-compatible.
Architecture
flowchart LR subgraph shared [Shared primitives] types[types.ts JSON-RPC and MCP types] rpc[json_rpc.ts framing and dispatch] end subgraph transports [Transports] stdio[StdioTransport stdin and stdout] http[HttpTransport Streamable HTTP] end server[McpServer tool resource prompt] --> rpc client[McpClient callTool readResource] --> rpc rpc --> types server --> transports client --> transports stdio -->|composes| stdlib1[std json streams io] http -->|composes| stdlib2[std http]Message flow (stdio server)
API sketches
All public APIs ship as unstable (
@experimental+UNSTABLEwarning)until vetted by the community and core team.
types.tsjson_rpc.tsMcpTransportinterfacestdio_transport.tshttp_transport.tsserver.tsclient.tsPackage layout
mcp/deno.json{ "name": "@std/mcp", "version": "0.1.0", "exports": { ".": "./mod.ts", "./types": "./types.ts", "./json-rpc": "./json_rpc.ts", "./stdio": "./stdio_transport.ts", "./http": "./http_transport.ts", "./server": "./server.ts", "./client": "./client.ts", "./permissions": "./permissions.ts" } }Granular exports mirror
json/deno.jsonandmsgpack/deno.json, allowing users to import only whatthey need (e.g.
@std/mcp/typesfor types-only usage in browser code).mcp/mod.tsFollows the pattern from
json/mod.ts:@moduletagUnstable API policy
Per the contributing guide, all public symbols ship
with:
The package starts at
0.1.0(pre-1.0) following std'sversioning proposal for unstable
packages.
Testing
Each source file has a paired
_test.ts. Tests use@std/assert(test-only,per contributing guide rule #4865). Test names follow the convention:
StdioTransport parses newline-delimited JSON messagesMcpServer handles initialize and returns server capabilitiesMcpClient callTool() sends tools/call and returns resultHTTP transport tests can use
@std/httptest utilities. Stdio transport testscan pipe
ReadableStream/WritableStreampairs without touching realstdin/stdout.
Spec targeting
Initial target:
2025-11-25(stable)The first implementation targets the current stable MCP specification:
initialize/initializedhandshakeTransport details follow
Transports (2025-06-18):
/mcpendpoint; POST for client-to-server;optional SSE for server-to-client streaming;
MCP-Protocol-VersionheaderForward compatibility:
2026-07-28(stateless RC)The
2026-07-28 release candidate
introduces significant changes:
@std/mcpinitializehandshakeMcpServer/McpClientlifecycle changes behind transport version flagMcp-Session-IdheaderHttpTransportsession management simplifiedMcp-Method/Mcp-NameheadersHttpTransportheader injection_meta_metafields; version negotiation moves to per-requestserver/discoverThe layered design keeps version-specific behavior behind
McpTransportandprotocol version options, so the type layer remains stable while transports
adapt:
Explicitly not supported
2024-11-052026-07-28(roots, sampling, server-side logging)Non-goals
directly (per
2026-07-28direction)responsibility;
@std/httpprovides the HTTP primitives2024-11-05schema.ts; automation can be added laterRepo-wide touchpoints
Adding a new package requires updates across the monorepo. These should land in
follow-up PRs after this proposal is accepted.
Preceding PR (required first)
Per CONTRIBUTING.md, add
mcp(/unstable)?to thescopeslist in.github/workflows/title.ymlbefore the implementation PR. This enables semantic PR titles like
feat(mcp): add protocol types.Implementation PR touchpoints
deno.json"@std/mcp": "jsr:@std/mcp@^0.1.0"toimports; add"./mcp"toworkspace.github/workflows/title.ymlmcp(/unstable)?toscopes(if not done in preceding PR)_tools/check_docs.ts"../mcp/mod.ts"toENTRY_POINTSREADME.md@std/mcprow to the packages table_tools/check_import_map.tsValidation
All changes must pass:
This runs lint, format check, browser compat check, and the full test suite.
Implementation phases
Phase 1: Types + JSON-RPC (foundation)
types.tswith full MCP type coveragejson_rpc.tswith message builders, parser, andJsonRpcPeerPhase 2: Stdio transport
stdio_transport.tscomposing@std/jsonstreamsPhase 3: Server API
server.tswithMcpServer, tool/resource/prompt registrationinitialize,tools/list,tools/call, etc.Phase 4: HTTP transport
http_transport.tsfor Streamable HTTP (server + client)@std/httptest serverPhase 5: Client API
client.tswithMcpClientPhase 6: Permissions (optional)
permissions.tswithrequirePermissions()helperpermissionsfield on tool definitionsOpen questions
1. Scope within std
Does the Deno core team consider an application-protocol package in-scope for
std? Std has historically been conservative — it provides primitives (
http,json,streams) rather than application frameworks. MCP is arguably closer to@std/http(protocol helpers) than to a web framework, but it is still anapplication-layer protocol tied to a specific ecosystem (LLM tooling).
Alternative: ship as
@denoland/mcpoutside std, with std packages asdependencies. This reduces maintenance burden on the core team but loses the
"official Deno MCP SDK" positioning.
2. Permission integration location
Should
permissions.tsship in core@std/mcpor as a separate export path(
@std/mcp/permissions)?@std/mcp/typesand@std/mcp/json-rpcruntime-neutraland browser-compatible
3. Server + client packaging
Should server and client APIs live in one package (
@std/mcp) or split?@std/http(server + client in one module)
@std/mcp-server,@std/mcp-client): smaller dependency footprintfor server-only users
Recommendation: one package with granular exports (as proposed), since most
users will only import
@std/mcp/serveror@std/mcp/client.4. Type maintenance strategy
Hand-maintained types vs. generated from the official
schema.ts?requires manual sync on spec updates
Recommendation: start hand-maintained; add generation tooling if spec churn
becomes burdensome.
5. Browser compatibility
types.tsandjson_rpc.tscan be browser-compatible (no Deno APIs).Transports and server/client require Deno. Should browser compat be tested via
deno task test:browser?Recommendation: mark
types.tsandjson_rpc.tsas browser compatible;transports and high-level APIs are Deno-only.
Alternatives considered
Use the official TypeScript SDK via npm
Deno supports npm packages, but:
Ship only types (no server/client)
A types-only package is useful but misses the "zero config single-file server"
value proposition. The layered design allows types-only usage via
@std/mcp/typesregardless.Ship only server (no client)
Many MCP use cases are server-only (exposing tools to an LLM host). However,
client support enables testing, scripting, and MCP-to-MCP proxy scenarios with
minimal extra code since client and server share transports and JSON-RPC core.
References
fy2ne commentedon Jul 3, 2026
Really well thought out RFC, the layered architecture is clean and the phase order makes sense. Happy to take the client side as you suggested.
A few things worth talking through before we start building:
2026-07-28 compat - The protocolVersion flag on HttpTransport handles the header changes fine, but the RC removes the initialize handshake entirely which means capability negotiation shifts out of McpServer lifecycle into per-request _meta. That bleeds into server and client dispatch, not just transport. Worth agreeing on the shape now before Phase 3 locks things in. Single McpServer branching internally on version, or versioned factories?
Streaming tool results not mentioned but the 2025-11-25 spec allows progressive streaming over SSE on HTTP. Intentional defer or just out of scope for now? Good to have a clear line on it early.
Std scope honestly the biggest unknown before we invest too much. Worth getting a core team member to drop a signal in this thread so we're not building toward a "not in std" wall.
On the workflow side, I'm thinking we just keep separate forks and each open our own PR to deno_std when ready, cleaner for the maintainers to review that way. No need to share access to anything. We can co-author commits when we overlap on something though, what email do you use for commits so I have it ready?
Starting the McpClient skeleton on my end now so we have something concrete to look at.
fy2ne commentedon Jul 3, 2026
On the open questions, I lean toward one package with granular exports as you proposed, and I'd hold off on the @denoland/mcp alternative until we get a signal from the core team. No strong opinion on permissions sub-path vs core, whatever the maintainers prefer.
fy2ne commentedon Jul 3, 2026
Hey @bartlomieju
before we get into implementation, would love a quick read on whether MCP belongs in std or as a separate @denoland/mcp package. Happy to move forward either way.
luk3skyw4lker commentedon Jul 3, 2026
Yeah, good questions back on the plan
On the compat side, I lean towards a versioned factory, that way a consumer has to be explicit on which version he wants to use and we avoid confusions
The streaming tool results were a intentional defer on my end, hadn't dug deep enough to include it in the plan for now, happy to leave it to you to plan and do a follow-up PR as an additional feature for the package after we're done
I do think the std scope would be very nice though, because the prtoocol is actually becoming as core and standardized as HTTP and other ones, but I agree that maintainers should definitely get a say in this.
Yep, agreed on the workflow, we might just have to coordinate a little bit with the boostrapping and stuff (since you started the skeleton I can start when that's merged in). My commit email for co-authoring is lucashenriqueblemos@gmail.com
fy2ne commentedon Jul 3, 2026
Versioned factories makes sense, cleaner API, I'll update the client skeleton to reflect that.
I'll plan out the streaming follow-up PR once the main package lands, happy to own it
On bootstrapping : i have the client skeleton ready locally but I want to wait for @bartlomieju to weigh in on the std scope question before I push anything.
Once we get that signal I'll open the skeleton PR first so you can align your foundation files against it. Shouldn't be long.
tomas-zijdemans commentedon Sep 26, 2026
I create a lot of MCP servers, so I'm very interested in what would make sense here.
However, I’m concerned about drift here. The official TypeScript SDK supports Deno and covers much of this proposal, including transports and client/server APIs. A second implementation would have to track protocol changes (that tend to be big...). What concrete Deno gap would
@std/mcpsolve that we couldn’t address upstream?