@n24q02m/mcp-core 1.19.0-beta.4 → 1.19.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/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # mcp-core
2
+
3
+ **Shared foundation for building MCP servers -- Streamable HTTP transport, OAuth 2.1, browser-based credential setup, and a shared embedding daemon.**
4
+
5
+ <!-- BEGIN: AUTO-GENERATED-CROSS-PROMO -->
6
+ <details>
7
+ <summary><strong>Sister projects from n24q02m</strong> (click to expand)</summary>
8
+
9
+ | Project | Tagline | Tag |
10
+ |---|---|---|
11
+ | [better-code-review-graph](https://github.com/n24q02m/better-code-review-graph) | Knowledge graph for token-efficient code reviews -- semantic search and call-... | MCP |
12
+ | [better-email-mcp](https://github.com/n24q02m/better-email-mcp) | IMAP/SMTP email for AI agents -- read, send, organize folders, and manage att... | MCP |
13
+ | [better-godot-mcp](https://github.com/n24q02m/better-godot-mcp) | Composite MCP server for Godot Engine -- 17 composite tools for AI-assisted g... | MCP |
14
+ | [better-notion-mcp](https://github.com/n24q02m/better-notion-mcp) | Markdown-first Notion for AI agents -- pages, databases, blocks, and comments... | MCP |
15
+ | [better-telegram-mcp](https://github.com/n24q02m/better-telegram-mcp) | Telegram for AI agents -- messages, chats, media, and contacts across both bo... | MCP |
16
+ | [claude-plugins](https://github.com/n24q02m/claude-plugins) | Claude Code plugin marketplace for the n24q02m MCP servers -- install web sea... | Marketplace |
17
+ | [imagine-mcp](https://github.com/n24q02m/imagine-mcp) | Image and video understanding + generation for AI agents -- across Gemini, Op... | MCP |
18
+ | [jules-task-archiver](https://github.com/n24q02m/jules-task-archiver) | Chrome Extension for bulk operations on Jules tasks via batchexecute API -- a... | Tooling |
19
+ | [mcp-core](https://github.com/n24q02m/mcp-core) | Shared foundation for building MCP servers -- Streamable HTTP transport, OAut... | MCP |
20
+ | [mnemo-mcp](https://github.com/n24q02m/mnemo-mcp) | Persistent AI memory with hybrid search and embedded sync. Open, free, unlimi... | MCP |
21
+ | [qwen3-embed](https://github.com/n24q02m/qwen3-embed) | Lightweight Qwen3 text embedding and reranking via ONNX Runtime and GGUF | Library |
22
+ | [skret](https://github.com/n24q02m/skret) | Secrets without the server. | CLI |
23
+ | [tacet](https://github.com/n24q02m/tacet) | TACET: a self-distilling neuro-symbolic cascade that amortises LLM cost in kn... | Tooling |
24
+ | [web-core](https://github.com/n24q02m/web-core) | Shared web infrastructure package for search, scraping, HTTP security, and st... | Library |
25
+ | [wet-mcp](https://github.com/n24q02m/wet-mcp) | Open-source MCP server for AI agents: web search, content extraction, and lib... | MCP |
26
+
27
+ </details>
28
+ <!-- END: AUTO-GENERATED-CROSS-PROMO -->
29
+
30
+ ## Table of contents
31
+
32
+ - [Packages](#packages)
33
+ - [What you get](#what-you-get)
34
+ - [Quick start (Python)](#quick-start-python)
35
+ - [Quick start (TypeScript)](#quick-start-typescript)
36
+ - [Documentation](#documentation)
37
+ - [Development](#development)
38
+ - [License](#license)
39
+
40
+
41
+
42
+ mcp-core is the shared foundation for the n24q02m MCP servers: a Streamable
43
+ HTTP transport, an OAuth 2.1 Authorization Server, lifecycle management,
44
+ install automation, and a shared embedding daemon.
45
+
46
+ `mcp-core` is the **functional successor** to the archived
47
+ [`mcp-relay-core`](https://github.com/n24q02m/mcp-relay-core). All crypto,
48
+ storage, OAuth, relay, and schema modules from `mcp-relay-core` ship under
49
+ the same paths in `mcp-core` (1:1 superset), so downstream MCP servers can
50
+ migrate with a pure import + dependency rename. See the
51
+ [Migration guide](https://mcp.n24q02m.com/servers/mcp-core/migration/)
52
+ for the rename table.
53
+
54
+ ## Packages
55
+
56
+ | Package | Language | Registry | Install |
57
+ |---------|----------|----------|---------|
58
+ | [`packages/core-py`](packages/core-py) | Python 3.13 | PyPI: [`n24q02m-mcp-core`](https://pypi.org/project/n24q02m-mcp-core/) | `pip install n24q02m-mcp-core` |
59
+ | [`packages/core-ts`](packages/core-ts) | TypeScript / Node 24 | npm: [`@n24q02m/mcp-core`](https://www.npmjs.com/package/@n24q02m/mcp-core) | `bun add @n24q02m/mcp-core` |
60
+ | [`packages/embedding-daemon`](packages/embedding-daemon) | Python 3.13 | PyPI: [`mcp-embedding-daemon`](https://pypi.org/project/mcp-embedding-daemon/) | `pip install mcp-embedding-daemon` |
61
+
62
+ All three packages share the same version (`semantic-release.toml` bumps both
63
+ Python `pyproject.toml` files plus the npm `package.json` in lockstep).
64
+
65
+ The Python core ships one optional extra for the LLM passthrough (litellm):
66
+ `pip install 'n24q02m-mcp-core[llm]'`.
67
+
68
+ ## What you get
69
+
70
+ ### `n24q02m-mcp-core` (Python) and `@n24q02m/mcp-core` (TypeScript)
71
+
72
+ These modules ship in **both** languages with a matching public API
73
+ (cross-language test vectors keep the crypto byte-for-byte identical):
74
+
75
+ - **`crypto/`** — ECDH P-256, AES-256-GCM, HKDF-SHA256 primitives.
76
+ Cross-language test vectors guarantee Python and TypeScript produce the
77
+ same ciphertext for the same input.
78
+ - **`storage/`** — `PerPluginStore`, the per-plugin encrypted credential
79
+ store. Single-user (stdio / HTTP) writes `~/.<plugin>-mcp/config.json`
80
+ encrypted with a machine-bound key; HTTP multi-user writes
81
+ `~/.<plugin>-mcp/subs/<sub>/config.json` encrypted with a key derived from
82
+ the `CREDENTIAL_SECRET` env var (salt `<plugin>:<sub>`). Pluggable
83
+ `CredentialBackend`s (`LocalFsBackend`, `CfKvBackend`) decouple the on-disk
84
+ layout from serverless deployments. Also ships session lock files and
85
+ config resolver helpers. The legacy shared `config.enc` file
86
+ (`storage.config_file`) is deprecated.
87
+ - **`auth/`** — the self-hosted OAuth 2.1 Authorization Server that downstream
88
+ servers actually run: `create_local_oauth_app` (Starlette ASGI app serving
89
+ `/authorize`, `/token`, the `/.well-known/oauth-*` metadata, and the
90
+ browser-rendered credential form), `render_credential_form`, the optional
91
+ shared-password gate at `/login` (`MCP_RELAY_PASSWORD`, empty disables it),
92
+ and `create_delegated_oauth_app` for upstream-redirect / device-code
93
+ multi-user flows.
94
+ - **`oauth/`** — OAuth 2.1 primitives consumed by `auth/`: `JWTIssuer`
95
+ (RS256), `SqliteUserStore` for multi-user mode, and `OAuthProvider`, the
96
+ legacy `mcp-relay-core` PKCE-over-relay provider retained for migration.
97
+ - **`relay/`** — `RelaySession`, `create_session`, `poll_for_result`,
98
+ `send_message` plus the EFF Diceware wordlist for passphrase generation.
99
+ This is the legacy `mcp-relay-core` ECDH relay-client path used by
100
+ `OAuthProvider`; the live setup UX is the `auth/` browser credential form.
101
+ - **`schema/`** — `RelayConfigSchema` TypedDict that downstream servers use
102
+ to declare their config form.
103
+ - **`transport/`** — `StreamableHTTPServer` wrapper around FastMCP /
104
+ `@modelcontextprotocol/sdk` Streamable HTTP transport, plus
105
+ `OAuthMiddleware` (RFC 6750 + RFC 9728 compliant Bearer validation).
106
+ - **`lifecycle/`** — `LifecycleLock` cross-platform file lock that prevents
107
+ two server instances from binding the same `(name, port)` pair.
108
+
109
+ ### Python-only modules (`n24q02m-mcp-core`)
110
+
111
+ These have no TypeScript counterpart yet — they back the Python MCP servers
112
+ (wet, mnemo, code-review-graph, telegram, imagine):
113
+
114
+ - **`llm/`** — a thin passthrough over [litellm](https://github.com/BerriAI/litellm)
115
+ so every server talks to cloud providers the same way. Async + sync wrappers
116
+ for `completion`, `embedding`, `rerank`, `image_generation`,
117
+ `video_generation` / `video_status` / `video_content`; a graceful capability
118
+ check against the litellm registry (`check_capability`, `list_models`,
119
+ `suggest_models`, `supports_vision`); multi-key CSV rotation per provider
120
+ (`rotate_keys`, `split_keys`); and a direct Vertex AI **Express** adapter
121
+ (`completion_express`) that bypasses litellm where its `vertex_ai/` route
122
+ ignores the Express API key. Provider keys follow the litellm convention —
123
+ `GEMINI_API_KEY`, `OPENAI_API_KEY`, `XAI_API_KEY`, `ANTHROPIC_API_KEY`,
124
+ `COHERE_API_KEY`, `JINA_AI_API_KEY`, `GOOGLE_VERTEX_EXPRESS_API_KEY` (see
125
+ `llm.providers.PROVIDER_KEY_ENV`); any unlisted provider falls back to
126
+ `<PROVIDER>_API_KEY`. Requires the optional extra:
127
+ `pip install 'n24q02m-mcp-core[llm]'`.
128
+ - **`chains.py`** — the capability provider-chain primitive every server shares
129
+ (exported at the top level): `resolve_backend` makes the 3-way
130
+ cloud / local / unavailable decision, `run_with_fallback` walks an ordered
131
+ list of providers and returns the first non-empty result, and
132
+ `local_enabled_from_env` reads the per-capability `DISABLE_LOCAL_<X>` toggle
133
+ (`DISABLE_LOCAL_SEARCH` / `DISABLE_LOCAL_BROWSER` / `DISABLE_LOCAL_EMBED` /
134
+ `DISABLE_LOCAL_RERANK`).
135
+ - **`http/`** — SSRF-safe HTTP clients (`get_ssrf_safe_async_client`,
136
+ `get_ssrf_safe_sync_client`, `vet_api_base`) that block requests to private /
137
+ loopback / link-local addresses. Used to vet every user-supplied `api_base`
138
+ before it reaches an outbound provider call.
139
+ - **`install/`** — `AgentInstaller` writes MCP server entries into Claude Code,
140
+ Cursor, Codex, Windsurf, and OpenCode config files. Ships the
141
+ `mcp-clean-state` console script for wiping local config / session state.
142
+
143
+ ### `mcp-embedding-daemon`
144
+
145
+ FastAPI HTTP server scaffold for the upcoming shared ONNX/GGUF embedding
146
+ backend. Currently exposes:
147
+
148
+ - `GET /health` — returns `{status, version}`
149
+ - `POST /embed` — returns 501 with a roadmap link (backend wiring lands in
150
+ the next release)
151
+ - `POST /rerank` — returns 501 with a roadmap link
152
+
153
+ CLI entry point: `mcp-embedding-daemon --host 127.0.0.1 --port 9800`.
154
+
155
+ ## Quick start (Python)
156
+
157
+ ```python
158
+ from mcp_core import RelaySession, create_session, decrypt
159
+ from mcp_core.transport.streamable_http import StreamableHTTPServer
160
+ from mcp_core.oauth import JWTIssuer
161
+ from mcp_core.transport.oauth_middleware import OAuthMiddleware
162
+ from fastmcp import FastMCP
163
+
164
+ mcp = FastMCP("my-server")
165
+
166
+ issuer = JWTIssuer("my-server")
167
+ issuer # Use issuer.issue_access_token(sub) / verify_access_token(token)
168
+
169
+ middleware = [OAuthMiddleware(issuer=issuer, resource_metadata_url="http://127.0.0.1:9876/.well-known/oauth-protected-resource")]
170
+ server = StreamableHTTPServer(mcp, port=9876, middleware=middleware)
171
+ server.run()
172
+ ```
173
+
174
+ ## Quick start (TypeScript)
175
+
176
+ ```typescript
177
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
178
+ import { JWTIssuer } from '@n24q02m/mcp-core/oauth'
179
+ import { OAuthMiddleware, StreamableHTTPServer } from '@n24q02m/mcp-core/transport'
180
+
181
+ const server = new McpServer({ name: 'my-server', version: '0.0.0' })
182
+ const issuer = new JWTIssuer('my-server')
183
+ await issuer.init()
184
+
185
+ const middleware = new OAuthMiddleware({
186
+ jwtIssuer: issuer,
187
+ resourceMetadataUrl: 'http://127.0.0.1:9876/.well-known/oauth-protected-resource'
188
+ })
189
+
190
+ const http = new StreamableHTTPServer({ server, port: 9876, oauthMiddleware: middleware })
191
+ await http.connect()
192
+ // Then mount http.handleRequest(req, res) on your http.Server / Express / Hono.
193
+ ```
194
+
195
+ ## Documentation
196
+
197
+ Full docs at **[mcp.n24q02m.com/servers/mcp-core/architecture/](https://mcp.n24q02m.com/servers/mcp-core/architecture/)** (Foundation library section in the MCP n24q02m unified docs site):
198
+
199
+ - [Architecture](https://mcp.n24q02m.com/servers/mcp-core/architecture/) -- transport, OAuth AS, lifecycle, multi-user primitives
200
+ - [Trust model](https://mcp.n24q02m.com/servers/mcp-core/trust-model/) -- threat model + key-handling guarantees
201
+ - [Migration](https://mcp.n24q02m.com/servers/mcp-core/migration/) -- breaking-change history and upgrade paths from `mcp-relay-core`
202
+ - [Shared services](https://mcp.n24q02m.com/servers/mcp-core/shared-services/) -- embedding daemon + ancillary docker-compose stack
203
+
204
+ Source of truth lives in [`n24q02m/claude-plugins/plugins/mcp-core/`](https://github.com/n24q02m/claude-plugins/tree/main/plugins/mcp-core). Edit there; this repo's `docs/` directory is intentionally minimal post-migration.
205
+
206
+ ## Development
207
+
208
+ ```bash
209
+ mise run setup # install runtimes + deps + pre-commit hooks
210
+ bun install # root TypeScript workspace install
211
+
212
+ # Python (per package)
213
+ cd packages/core-py
214
+ uv sync --group dev
215
+ uv run pytest
216
+ uv run ty check
217
+ uv run ruff check .
218
+
219
+ # TypeScript
220
+ cd packages/core-ts
221
+ bun run test
222
+ bun run check
223
+ bun run build
224
+ ```
225
+
226
+ ## License
227
+
228
+ MIT
package/build/index.d.ts CHANGED
@@ -6,7 +6,7 @@ export { InMemoryAuthCache, type IOAuthSessionCache, OAuthProvider, type OAuthPr
6
6
  export { type IUserCredentialStore, SqliteUserStore } from './oauth/user-store.js';
7
7
  export { tryOpenBrowser } from './relay/browser.js';
8
8
  export { createSession, generatePassphrase, notifyComplete, pollForResponses, pollForResult, type RelaySession, sendMessage } from './relay/client.js';
9
- export { buildOpenRelayHandler, type OpenRelayHandlerOptions, type OpenRelayResult, registerOpenRelayTool, type ToolRegistrar } from './relay/tool-helpers.js';
9
+ export { buildOpenRelayHandler, type ElicitationServer, type OpenRelayHandlerOptions, type OpenRelayResult, registerOpenRelayTool, type ToolRegistrar } from './relay/tool-helpers.js';
10
10
  export type * from './schema/types.js';
11
11
  export { deleteConfig, exportConfig, importConfig, listConfigs, readConfig, scheduleReloadExit, writeConfig } from './storage/config-file.js';
12
12
  export { clearMode, getMode, type ServerMode, setLocalMode } from './storage/mode.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,2BAA2B,EAC3B,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,uBAAuB,EACvB,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,oBAAoB,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACzB,MAAM,iBAAiB,CAAA;AACxB,cAAc,mBAAmB,CAAA;AAEjC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EACL,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,KAAK,oBAAoB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,KAAK,YAAY,EACjB,WAAW,EACZ,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,qBAAqB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,aAAa,EACnB,MAAM,yBAAyB,CAAA;AAChC,mBAAmB,mBAAmB,CAAA;AACtC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,WAAW,EACZ,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrF,cAAc,uBAAuB,CAAA;AACrC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,WAAW,EAChB,gBAAgB,EACjB,MAAM,2BAA2B,CAAA;AAElC,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,aAAa,EACd,MAAM,6BAA6B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,2BAA2B,EAC3B,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,uBAAuB,EACvB,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,oBAAoB,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACzB,MAAM,iBAAiB,CAAA;AACxB,cAAc,mBAAmB,CAAA;AAEjC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EACL,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,KAAK,oBAAoB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,KAAK,YAAY,EACjB,WAAW,EACZ,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,aAAa,EACnB,MAAM,yBAAyB,CAAA;AAChC,mBAAmB,mBAAmB,CAAA;AACtC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,WAAW,EACZ,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrF,cAAc,uBAAuB,CAAA;AACrC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,WAAW,EAChB,gBAAgB,EACjB,MAAM,2BAA2B,CAAA;AAElC,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,aAAa,EACd,MAAM,6BAA6B,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,2EAA2E;AAC3E,OAAO,EACL,2BAA2B,EAI3B,uBAAuB,EACvB,mBAAmB,EAQnB,yBAAyB,EAGzB,oBAAoB,EAKrB,MAAM,iBAAiB,CAAA;AACxB,cAAc,mBAAmB,CAAA;AACjC,kDAAkD;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EACL,iBAAiB,EAEjB,aAAa,EAGd,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAA6B,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,aAAa,EAEb,WAAW,EACZ,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,qBAAqB,EAGrB,qBAAqB,EAEtB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,WAAW,EACZ,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAmB,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrF,cAAc,uBAAuB,CAAA;AACrC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAElB,gBAAgB,EACjB,MAAM,2BAA2B,CAAA;AAClC,uEAAuE;AACvE,OAAO,EAGL,aAAa,EACd,MAAM,6BAA6B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,2EAA2E;AAC3E,OAAO,EACL,2BAA2B,EAI3B,uBAAuB,EACvB,mBAAmB,EAQnB,yBAAyB,EAGzB,oBAAoB,EAKrB,MAAM,iBAAiB,CAAA;AACxB,cAAc,mBAAmB,CAAA;AACjC,kDAAkD;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EACL,iBAAiB,EAEjB,aAAa,EAGd,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAA6B,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,aAAa,EAEb,WAAW,EACZ,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,qBAAqB,EAIrB,qBAAqB,EAEtB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,WAAW,EACZ,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAmB,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrF,cAAc,uBAAuB,CAAA;AACrC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAElB,gBAAgB,EACjB,MAAM,2BAA2B,CAAA;AAClC,uEAAuE;AACvE,OAAO,EAGL,aAAa,EACd,MAAM,6BAA6B,CAAA"}
@@ -3,28 +3,52 @@
3
3
  *
4
4
  * After the stdio-pure + http-multi-user split, the relay config form is
5
5
  * served by the HTTP server itself at ``<PUBLIC_URL>/authorize``. The
6
- * ``config__open_relay`` tool simply returns that URL and best-effort opens
7
- * the user's default browser. There is no daemon-bridge discovery, no
8
- * respawn, and no session deduplication the HTTP server's own session
9
- * tracking handles concurrent setup attempts.
6
+ * ``config__open_relay`` tool directs the user to that URL.
7
+ *
8
+ * When the connected client declares URL-mode elicitation
9
+ * (``capabilities.elicitation.url``, SEP-1036), the tool asks the client to
10
+ * present a consent prompt and open the URL via ``Server.elicitInput`` so the
11
+ * credential form is opened out-of-band without the LLM in the loop. Clients
12
+ * that do NOT declare the capability fall back to the legacy behaviour: the
13
+ * tool returns the URL and best-effort opens the user's default browser
14
+ * server-side. There is no daemon-bridge discovery, no respawn, and no
15
+ * session deduplication — the HTTP server's own session tracking handles
16
+ * concurrent setup attempts.
10
17
  *
11
18
  * In stdio mode, the relay form does not exist; the tool returns
12
19
  * ``stdio_unsupported`` so plugin code can render a "switch to HTTP mode"
13
20
  * message instead of misleading the user with an unreachable URL.
14
21
  */
22
+ import type { ClientCapabilities, ElicitRequestURLParams, ElicitResult } from '@modelcontextprotocol/sdk/types.js';
15
23
  export interface OpenRelayResult {
16
24
  url: string;
17
25
  browserOpened: boolean;
18
26
  status: 'configured' | 'unconfigured' | 'expired' | 'session_active' | 'stdio_unsupported';
27
+ elicitation?: 'accepted' | 'declined' | 'cancelled';
28
+ }
29
+ /**
30
+ * Minimal surface required to drive URL-mode elicitation. Satisfied by the
31
+ * low-level `Server` from `@modelcontextprotocol/sdk` (`mcp.server`), which
32
+ * exposes both `getClientCapabilities` and `elicitInput`.
33
+ */
34
+ export interface ElicitationServer {
35
+ getClientCapabilities: () => ClientCapabilities | undefined;
36
+ elicitInput: (params: ElicitRequestURLParams) => Promise<ElicitResult>;
19
37
  }
20
38
  export interface OpenRelayHandlerOptions {
21
39
  serverName: string;
22
40
  publicUrl: string | null;
41
+ elicitation?: ElicitationServer;
23
42
  }
24
43
  /**
25
44
  * Build the `open_relay` handler closure. Tests can call
26
45
  * ``buildOpenRelayHandler({ serverName, publicUrl })`` directly without
27
46
  * needing a real MCP server instance.
47
+ *
48
+ * When `elicitation` is supplied AND the client declared URL-mode
49
+ * elicitation, the handler drives `elicitInput` and returns the accept /
50
+ * decline / cancel outcome. Otherwise it returns the legacy dict
51
+ * (`{ url, browserOpened, status }`) unchanged.
28
52
  */
29
53
  export declare function buildOpenRelayHandler(options: OpenRelayHandlerOptions): () => Promise<OpenRelayResult>;
30
54
  /**
@@ -46,12 +70,16 @@ export interface ToolRegistrar {
46
70
  *
47
71
  * ```ts
48
72
  * import { registerOpenRelayTool } from '@n24q02m/mcp-core'
49
- * registerOpenRelayTool(mcp, SERVER_NAME, PUBLIC_URL)
73
+ * registerOpenRelayTool(mcp, SERVER_NAME, PUBLIC_URL, mcp.server)
50
74
  * ```
51
75
  *
52
76
  * Pass ``null`` for ``publicUrl`` when the server is running in stdio mode;
53
77
  * the tool will return ``status: 'stdio_unsupported'`` so the caller can
54
78
  * surface a clear "switch to HTTP mode" message.
79
+ *
80
+ * Pass the low-level `Server` (`mcp.server`) as ``elicitation`` to enable
81
+ * URL-mode elicitation for capable clients. When omitted, the tool keeps
82
+ * the legacy browser-open behaviour for every client.
55
83
  */
56
- export declare function registerOpenRelayTool(mcp: ToolRegistrar, serverName: string, publicUrl: string | null): void;
84
+ export declare function registerOpenRelayTool(mcp: ToolRegistrar, serverName: string, publicUrl: string | null, elicitation?: ElicitationServer): void;
57
85
  //# sourceMappingURL=tool-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-helpers.d.ts","sourceRoot":"","sources":["../../src/relay/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,OAAO,CAAA;IACtB,MAAM,EAAE,YAAY,GAAG,cAAc,GAAG,SAAS,GAAG,gBAAgB,GAAG,mBAAmB,CAAA;CAC3F;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,CAStG;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,KAAK,IAAI,CAAA;CACxG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAS5G"}
1
+ {"version":3,"file":"tool-helpers.d.ts","sourceRoot":"","sources":["../../src/relay/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAGlH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,OAAO,CAAA;IACtB,MAAM,EAAE,YAAY,GAAG,cAAc,GAAG,SAAS,GAAG,gBAAgB,GAAG,mBAAmB,CAAA;IAC1F,WAAW,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,WAAW,CAAA;CACpD;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,qBAAqB,EAAE,MAAM,kBAAkB,GAAG,SAAS,CAAA;IAC3D,WAAW,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;CACvE;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,CAAC,EAAE,iBAAiB,CAAA;CAChC;AAaD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,CAyBtG;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,KAAK,IAAI,CAAA;CACxG;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,aAAa,EAClB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,WAAW,CAAC,EAAE,iBAAiB,GAC9B,IAAI,CASN"}
@@ -3,20 +3,42 @@
3
3
  *
4
4
  * After the stdio-pure + http-multi-user split, the relay config form is
5
5
  * served by the HTTP server itself at ``<PUBLIC_URL>/authorize``. The
6
- * ``config__open_relay`` tool simply returns that URL and best-effort opens
7
- * the user's default browser. There is no daemon-bridge discovery, no
8
- * respawn, and no session deduplication the HTTP server's own session
9
- * tracking handles concurrent setup attempts.
6
+ * ``config__open_relay`` tool directs the user to that URL.
7
+ *
8
+ * When the connected client declares URL-mode elicitation
9
+ * (``capabilities.elicitation.url``, SEP-1036), the tool asks the client to
10
+ * present a consent prompt and open the URL via ``Server.elicitInput`` so the
11
+ * credential form is opened out-of-band without the LLM in the loop. Clients
12
+ * that do NOT declare the capability fall back to the legacy behaviour: the
13
+ * tool returns the URL and best-effort opens the user's default browser
14
+ * server-side. There is no daemon-bridge discovery, no respawn, and no
15
+ * session deduplication — the HTTP server's own session tracking handles
16
+ * concurrent setup attempts.
10
17
  *
11
18
  * In stdio mode, the relay form does not exist; the tool returns
12
19
  * ``stdio_unsupported`` so plugin code can render a "switch to HTTP mode"
13
20
  * message instead of misleading the user with an unreachable URL.
14
21
  */
22
+ import { randomUUID } from 'node:crypto';
15
23
  import { tryOpenBrowser } from './browser.js';
24
+ // Maps the client's ElicitResult.action to the tool's outcome field.
25
+ const ELICIT_ACTION_MAP = {
26
+ accept: 'accepted',
27
+ decline: 'declined',
28
+ cancel: 'cancelled'
29
+ };
30
+ function clientSupportsUrlElicitation(server) {
31
+ return server.getClientCapabilities()?.elicitation?.url != null;
32
+ }
16
33
  /**
17
34
  * Build the `open_relay` handler closure. Tests can call
18
35
  * ``buildOpenRelayHandler({ serverName, publicUrl })`` directly without
19
36
  * needing a real MCP server instance.
37
+ *
38
+ * When `elicitation` is supplied AND the client declared URL-mode
39
+ * elicitation, the handler drives `elicitInput` and returns the accept /
40
+ * decline / cancel outcome. Otherwise it returns the legacy dict
41
+ * (`{ url, browserOpened, status }`) unchanged.
20
42
  */
21
43
  export function buildOpenRelayHandler(options) {
22
44
  return async function openRelay() {
@@ -24,6 +46,22 @@ export function buildOpenRelayHandler(options) {
24
46
  return { url: '', browserOpened: false, status: 'stdio_unsupported' };
25
47
  }
26
48
  const url = `${options.publicUrl.replace(/\/$/, '')}/authorize`;
49
+ const { elicitation } = options;
50
+ if (elicitation && clientSupportsUrlElicitation(elicitation)) {
51
+ const message = `Open the ${options.serverName} configuration page to enter your credentials securely in your browser.`;
52
+ const result = await elicitation.elicitInput({
53
+ mode: 'url',
54
+ message,
55
+ url,
56
+ elicitationId: randomUUID()
57
+ });
58
+ return {
59
+ url,
60
+ browserOpened: false,
61
+ status: 'unconfigured',
62
+ elicitation: ELICIT_ACTION_MAP[result.action] ?? 'accepted'
63
+ };
64
+ }
27
65
  const opened = await tryOpenBrowser(url);
28
66
  return { url, browserOpened: !!opened, status: 'unconfigured' };
29
67
  };
@@ -36,15 +74,19 @@ export function buildOpenRelayHandler(options) {
36
74
  *
37
75
  * ```ts
38
76
  * import { registerOpenRelayTool } from '@n24q02m/mcp-core'
39
- * registerOpenRelayTool(mcp, SERVER_NAME, PUBLIC_URL)
77
+ * registerOpenRelayTool(mcp, SERVER_NAME, PUBLIC_URL, mcp.server)
40
78
  * ```
41
79
  *
42
80
  * Pass ``null`` for ``publicUrl`` when the server is running in stdio mode;
43
81
  * the tool will return ``status: 'stdio_unsupported'`` so the caller can
44
82
  * surface a clear "switch to HTTP mode" message.
83
+ *
84
+ * Pass the low-level `Server` (`mcp.server`) as ``elicitation`` to enable
85
+ * URL-mode elicitation for capable clients. When omitted, the tool keeps
86
+ * the legacy browser-open behaviour for every client.
45
87
  */
46
- export function registerOpenRelayTool(mcp, serverName, publicUrl) {
47
- const handler = buildOpenRelayHandler({ serverName, publicUrl });
88
+ export function registerOpenRelayTool(mcp, serverName, publicUrl, elicitation) {
89
+ const handler = buildOpenRelayHandler({ serverName, publicUrl, elicitation });
48
90
  mcp.tool({
49
91
  name: 'config__open_relay',
50
92
  description: `Open the relay configuration form for ${serverName} in the user's browser.`
@@ -1 +1 @@
1
- {"version":3,"file":"tool-helpers.js","sourceRoot":"","sources":["../../src/relay/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAa7C;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgC;IACpE,OAAO,KAAK,UAAU,SAAS;QAC7B,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACvB,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAA;QACvE,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAA;QAC/D,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAA;QACxC,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA;IACjE,CAAC,CAAA;AACH,CAAC;AAWD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAkB,EAAE,UAAkB,EAAE,SAAwB;IACpG,MAAM,OAAO,GAAG,qBAAqB,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAA;IAChE,GAAG,CAAC,IAAI,CACN;QACE,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,yCAAyC,UAAU,yBAAyB;KAC1F,EACD,OAAO,CACR,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"tool-helpers.js","sourceRoot":"","sources":["../../src/relay/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAyB7C,qEAAqE;AACrE,MAAM,iBAAiB,GAA0D;IAC/E,MAAM,EAAE,UAAU;IAClB,OAAO,EAAE,UAAU;IACnB,MAAM,EAAE,WAAW;CACpB,CAAA;AAED,SAAS,4BAA4B,CAAC,MAAyB;IAC7D,OAAO,MAAM,CAAC,qBAAqB,EAAE,EAAE,WAAW,EAAE,GAAG,IAAI,IAAI,CAAA;AACjE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgC;IACpE,OAAO,KAAK,UAAU,SAAS;QAC7B,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACvB,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAA;QACvE,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAA;QAC/D,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAA;QAC/B,IAAI,WAAW,IAAI,4BAA4B,CAAC,WAAW,CAAC,EAAE,CAAC;YAC7D,MAAM,OAAO,GAAG,YAAY,OAAO,CAAC,UAAU,yEAAyE,CAAA;YACvH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,WAAW,CAAC;gBAC3C,IAAI,EAAE,KAAK;gBACX,OAAO;gBACP,GAAG;gBACH,aAAa,EAAE,UAAU,EAAE;aAC5B,CAAC,CAAA;YACF,OAAO;gBACL,GAAG;gBACH,aAAa,EAAE,KAAK;gBACpB,MAAM,EAAE,cAAc;gBACtB,WAAW,EAAE,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,UAAU;aAC5D,CAAA;QACH,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAA;QACxC,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA;IACjE,CAAC,CAAA;AACH,CAAC;AAWD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAAkB,EAClB,UAAkB,EAClB,SAAwB,EACxB,WAA+B;IAE/B,MAAM,OAAO,GAAG,qBAAqB,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAA;IAC7E,GAAG,CAAC,IAAI,CACN;QACE,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,yCAAyC,UAAU,yBAAyB;KAC1F,EACD,OAAO,CACR,CAAA;AACH,CAAC"}
package/package.json CHANGED
@@ -1,13 +1,18 @@
1
1
  {
2
2
  "name": "@n24q02m/mcp-core",
3
- "version": "1.19.0-beta.4",
3
+ "version": "1.19.0",
4
4
  "description": "Unified MCP Streamable HTTP 2025-11-25 transport, OAuth 2.1 AS, lifecycle, install",
5
5
  "keywords": [
6
6
  "mcp",
7
7
  "model-context-protocol",
8
8
  "streamable-http",
9
9
  "oauth21",
10
- "transport"
10
+ "transport",
11
+ "claude",
12
+ "cursor",
13
+ "copilot",
14
+ "codex",
15
+ "opencode"
11
16
  ],
12
17
  "author": {
13
18
  "name": "n24q02m",