langfx.js 0.1.0-alpha.0 → 0.1.0-alpha.1

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 CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  Language as functions, in TypeScript. Build agents in browsers, workers, and apps without a Python agent backend.
4
4
 
5
- **Status: initial alpha release candidate (`0.1.0-alpha.0`).** OpenAI, Anthropic, and Gemini support text/image inputs, client tools, and streaming tool events through fixture-tested transports. The runtime includes prompt composition, structured JSON streaming, bounded agents, invocation traces/logs/progress, retries, and local response caching. Gemini has passed the live smoke suite; live OpenAI/Anthropic behavior and provider browser CORS remain unverified. APIs may change during alpha. See [implementation status](docs/IMPLEMENTATION_STATUS.md) and the [opt-in live smoke runner](docs/LIVE_TESTING.md).
5
+ **Status: alpha (`0.1.0-alpha.1`).** OpenAI, Anthropic, and Gemini support text/image inputs, client tools, and streaming tool events through fixture-tested transports. The runtime includes prompt composition, structured JSON streaming, bounded agents, invocation traces/logs/progress, retries, and local response caching. Gemini has passed the live smoke suite; live OpenAI/Anthropic behavior and provider browser CORS remain unverified. APIs may change during alpha. See [implementation status](docs/IMPLEMENTATION_STATUS.md) and the [opt-in live smoke runner](docs/LIVE_TESTING.md).
6
6
 
7
7
  The proposal is based on [`free-solo/langfx`](https://github.com/free-solo/langfx) at commit `2a1ea4e8dcd7075bf745ad707f901ece8546f47d`, examined on September 16, 2026.
8
8
 
9
9
  ## Install the alpha
10
10
 
11
- Once published, install with `npm install langfx.js@alpha`. Zod users also install `zod`; the core has no runtime dependencies. This checkout prepares the release; it does not imply the package is already available on npm. See [release notes and publishing procedure](docs/RELEASING.md).
11
+ Install with `npm install langfx.js@alpha`. Zod users also install `zod`; the core has no runtime dependencies. See [release notes and publishing procedure](docs/RELEASING.md).
12
12
 
13
13
  ## Run the core
14
14
 
@@ -105,3 +105,7 @@ Local operations such as `Template.render()`, schema validation, and message con
105
105
  Apache-2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE) for upstream attribution.
106
106
 
107
107
  For named class unions and constructor-style responses, see the default [Python-style protocol](docs/PYTHON_PROTOCOL.md).
108
+
109
+ ## MCP
110
+
111
+ The optional `langfx.js/mcp` module follows Python Langfx’s client/session/tool APIs for remote Streamable HTTP servers, validated discovery and calls, cancellation, and explicit agent tool selection. Node/desktop hosts can use `StdioMcpClient.fromCommand` from `langfx.js/mcp/node` for local servers, including Python FastMCP. Install its optional `@modelcontextprotocol/client` 2.x peer. See [MCP usage and compatibility](docs/MCP.md). MCP support is included starting with `0.1.0-alpha.1`.
@@ -0,0 +1,75 @@
1
+ import type { CallToolResult, Tool as ToolDefinition, Transport, StreamableHTTPClientTransportOptions } from '@modelcontextprotocol/client';
2
+ import { ToolExecutionError } from '../errors.js';
3
+ import { ToolMessage } from '../message.js';
4
+ import type { RegisteredTool } from '../tools.js';
5
+ export interface McpRequestOptions {
6
+ readonly signal?: AbortSignal;
7
+ readonly timeout?: number;
8
+ }
9
+ export interface McpCallOptions extends McpRequestOptions {
10
+ readonly returnsMessage?: boolean;
11
+ readonly toolCallId?: string;
12
+ }
13
+ export interface McpClientOptions {
14
+ /** Fresh, owned SDK transport for each session. Enables host-specific bridges. */
15
+ readonly transport: () => Transport;
16
+ readonly name?: string;
17
+ readonly version?: string;
18
+ }
19
+ export type McpHttpOptions = Pick<StreamableHTTPClientTransportOptions, 'fetch' | 'authProvider' | 'requestInit'>;
20
+ export type McpTools = Readonly<Record<string, McpTool>>;
21
+ /** Async equivalent of Python's client; importing core never loads the SDK. */
22
+ export declare class McpClient {
23
+ private readonly options;
24
+ private tools;
25
+ constructor(options: McpClientOptions);
26
+ static fromUrl(url: string | URL, options?: McpHttpOptions): McpClient;
27
+ session(): McpSession;
28
+ knownTools(): McpTools | undefined;
29
+ listTools(options?: McpRequestOptions & {
30
+ readonly refresh?: boolean;
31
+ }): Promise<McpTools>;
32
+ withSession<T>(fn: (session: McpSession) => Promise<T>, options?: McpRequestOptions): Promise<T>;
33
+ }
34
+ /** One owned connection. Closed/failed sessions cannot be reopened; create a new session. */
35
+ export declare class McpSession {
36
+ private readonly options;
37
+ private readonly sdk;
38
+ private transport;
39
+ private state;
40
+ private readonly lifetime;
41
+ private closing;
42
+ private tools;
43
+ constructor(options: McpClientOptions);
44
+ connect(options?: McpRequestOptions): Promise<this>;
45
+ close(): Promise<void>;
46
+ private request;
47
+ listTools(options?: McpRequestOptions): Promise<McpTools>;
48
+ callTool(name: string, args: Record<string, unknown>, options: McpCallOptions & {
49
+ returnsMessage: true;
50
+ }): Promise<ToolMessage>;
51
+ callTool(name: string, args?: Record<string, unknown>, options?: McpCallOptions): Promise<unknown>;
52
+ /** Explicit selection is required before remote tools are exposed to an agent. */
53
+ agentTools(names: readonly string[], options?: McpRequestOptions): Promise<readonly RegisteredTool[]>;
54
+ }
55
+ export declare class McpToolError extends ToolExecutionError {
56
+ readonly toolName: string;
57
+ readonly response: CallToolResult;
58
+ constructor(toolName: string, response: CallToolResult);
59
+ }
60
+ /** Runtime tool descriptor replaces Python's dynamically generated symbolic class. */
61
+ export declare class McpTool {
62
+ readonly definition: Readonly<ToolDefinition>;
63
+ private readonly inputValidator;
64
+ private readonly outputValidator;
65
+ constructor(definition: ToolDefinition);
66
+ get name(): string;
67
+ inputParameters(args: unknown): Record<string, unknown>;
68
+ validateOutput(result: CallToolResult): void;
69
+ call(args: Record<string, unknown>, session: McpSession, options: McpCallOptions & {
70
+ returnsMessage: true;
71
+ }): Promise<ToolMessage>;
72
+ call(args: Record<string, unknown>, session: McpSession, options?: McpCallOptions): Promise<unknown>;
73
+ asTool(session: McpSession): RegisteredTool;
74
+ static resultToMessage(result: CallToolResult, toolCallId?: string): ToolMessage;
75
+ }
@@ -0,0 +1,257 @@
1
+ import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';
2
+ import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/client/validators/cf-worker';
3
+ import { abortable, cancellationScope, checkCancelled } from '../cancellation.js';
4
+ import { ToolExecutionError, UnsupportedCapabilityError } from '../errors.js';
5
+ import { Image, Modality, ToolMessage } from '../message.js';
6
+ const validator = new CfWorkerJsonSchemaValidator();
7
+ /** Async equivalent of Python's client; importing core never loads the SDK. */
8
+ export class McpClient {
9
+ options;
10
+ tools;
11
+ constructor(options) {
12
+ this.options = options;
13
+ }
14
+ static fromUrl(url, options = {}) {
15
+ const endpoint = new URL(url);
16
+ if (!['http:', 'https:'].includes(endpoint.protocol) || endpoint.username || endpoint.password)
17
+ throw new TypeError('Expected an HTTP(S) MCP endpoint without URL credentials.');
18
+ return new McpClient({ transport: () => new StreamableHTTPClientTransport(endpoint, options) });
19
+ }
20
+ session() { return new McpSession(this.options); }
21
+ knownTools() { return this.tools; }
22
+ async listTools(options = {}) {
23
+ checkCancelled(options.signal);
24
+ if (this.tools && !options.refresh)
25
+ return this.tools;
26
+ return this.withSession(async (session) => this.tools = await session.listTools(options), options);
27
+ }
28
+ async withSession(fn, options = {}) {
29
+ const session = this.session();
30
+ await session.connect(options);
31
+ let failed = false;
32
+ try {
33
+ return await fn(session);
34
+ }
35
+ catch (error) {
36
+ failed = true;
37
+ throw error;
38
+ }
39
+ finally {
40
+ if (failed)
41
+ await session.close().catch(() => { });
42
+ else
43
+ await session.close();
44
+ }
45
+ }
46
+ }
47
+ /** One owned connection. Closed/failed sessions cannot be reopened; create a new session. */
48
+ export class McpSession {
49
+ options;
50
+ sdk;
51
+ transport;
52
+ state = 'new';
53
+ lifetime = new AbortController();
54
+ closing;
55
+ tools;
56
+ constructor(options) {
57
+ this.options = options;
58
+ this.sdk = new Client({ name: options.name ?? 'langfx.js', version: options.version ?? '0.1.0' }, { jsonSchemaValidator: validator });
59
+ // Unexpected closure must preserve SDK errors (including handshake failures).
60
+ // Only explicit close() cancels the lifetime; the SDK rejects pending requests.
61
+ this.sdk.onclose = () => { this.state = 'closed'; };
62
+ }
63
+ async connect(options = {}) {
64
+ if (this.state !== 'new')
65
+ throw new Error('MCP session cannot be re-entered.');
66
+ checkCancelled(options.signal);
67
+ this.state = 'connecting';
68
+ const scope = cancellationScope([options.signal, this.lifetime.signal]);
69
+ try {
70
+ this.transport = this.options.transport();
71
+ await abortable(this.sdk.connect(this.transport, { ...options, signal: scope.signal }), scope.signal);
72
+ checkCancelled(scope.signal);
73
+ this.state = 'open';
74
+ return this;
75
+ }
76
+ catch (error) {
77
+ await this.close().catch(() => { });
78
+ throw error;
79
+ }
80
+ finally {
81
+ scope.dispose();
82
+ }
83
+ }
84
+ close() {
85
+ if (this.closing)
86
+ return this.closing;
87
+ this.state = 'closed';
88
+ this.lifetime.abort();
89
+ this.closing = (async () => {
90
+ try {
91
+ if (this.transport instanceof StreamableHTTPClientTransport && this.transport.sessionId)
92
+ await abortable(this.transport.terminateSession(), AbortSignal.timeout(5_000));
93
+ }
94
+ finally {
95
+ await this.sdk.close();
96
+ }
97
+ })();
98
+ return this.closing;
99
+ }
100
+ async request(options, fn) {
101
+ if (this.state !== 'open')
102
+ throw new Error('MCP session is not connected.');
103
+ const scope = cancellationScope([options.signal, this.lifetime.signal]);
104
+ try {
105
+ checkCancelled(scope.signal);
106
+ return await abortable(fn({ ...options, signal: scope.signal }), scope.signal);
107
+ }
108
+ catch (error) {
109
+ checkCancelled(scope.signal);
110
+ throw error;
111
+ }
112
+ finally {
113
+ scope.dispose();
114
+ }
115
+ }
116
+ async listTools(options = {}) {
117
+ return this.request(options, async (request) => {
118
+ const tools = Object.create(null);
119
+ // SDK 2 aggregates pages and enforces its pagination limits internally.
120
+ const result = await this.sdk.listTools({}, { ...request, cacheMode: 'refresh' });
121
+ for (const definition of result.tools) {
122
+ if (Object.hasOwn(tools, definition.name))
123
+ throw new Error(`Duplicate MCP tool: ${definition.name}`);
124
+ tools[definition.name] = new McpTool(definition);
125
+ }
126
+ this.tools = Object.freeze(tools);
127
+ return this.tools;
128
+ });
129
+ }
130
+ async callTool(name, args = {}, options = {}) {
131
+ if (!this.tools)
132
+ await this.listTools(options);
133
+ const tool = this.tools[name];
134
+ if (!tool)
135
+ throw new ToolExecutionError(`Unknown MCP tool: ${name}`);
136
+ const input = tool.inputParameters(args);
137
+ return this.request(options, async (request) => {
138
+ const result = await this.sdk.callTool({ name, arguments: input }, request);
139
+ if (result.isError)
140
+ throw new McpToolError(name, result);
141
+ tool.validateOutput(result);
142
+ const message = McpTool.resultToMessage(result, options.toolCallId ?? name);
143
+ if (options.returnsMessage)
144
+ return message;
145
+ if (hasResult(result.structuredContent))
146
+ return result.structuredContent.result;
147
+ if (message.parts.some(part => part instanceof Modality))
148
+ return message;
149
+ return message.text;
150
+ });
151
+ }
152
+ /** Explicit selection is required before remote tools are exposed to an agent. */
153
+ async agentTools(names, options = {}) {
154
+ const tools = await this.listTools(options);
155
+ if (new Set(names).size !== names.length)
156
+ throw new TypeError('Duplicate MCP tool selection.');
157
+ return names.map(name => {
158
+ const tool = tools[name];
159
+ if (!tool)
160
+ throw new ToolExecutionError(`Unknown MCP tool: ${name}`);
161
+ return tool.asTool(this);
162
+ });
163
+ }
164
+ }
165
+ export class McpToolError extends ToolExecutionError {
166
+ toolName;
167
+ response;
168
+ constructor(toolName, response) {
169
+ super(`MCP tool ${toolName} returned an error.`);
170
+ this.toolName = toolName;
171
+ this.response = response;
172
+ }
173
+ }
174
+ /** Runtime tool descriptor replaces Python's dynamically generated symbolic class. */
175
+ export class McpTool {
176
+ definition;
177
+ inputValidator;
178
+ outputValidator;
179
+ constructor(definition) {
180
+ this.definition = freezeData(structuredClone(definition));
181
+ this.inputValidator = validator.getValidator(structuredClone(this.definition.inputSchema));
182
+ this.outputValidator = this.definition.outputSchema ? validator.getValidator(structuredClone(this.definition.outputSchema)) : undefined;
183
+ }
184
+ get name() { return this.definition.name; }
185
+ inputParameters(args) {
186
+ assertJson(args);
187
+ const input = structuredClone(args);
188
+ const result = this.inputValidator(input);
189
+ if (!result.valid)
190
+ throw new ToolExecutionError(`Invalid arguments for MCP tool ${this.name}: ${result.errorMessage}`);
191
+ return result.data;
192
+ }
193
+ validateOutput(result) {
194
+ if (!this.outputValidator)
195
+ return;
196
+ if (result.structuredContent === undefined || !this.outputValidator(result.structuredContent).valid)
197
+ throw new ToolExecutionError(`Invalid structured output from MCP tool ${this.name}.`);
198
+ }
199
+ call(args, session, options = {}) { return session.callTool(this.name, args, options); }
200
+ asTool(session) {
201
+ if (!/^[A-Za-z_][A-Za-z0-9_-]{0,63}$/.test(this.name))
202
+ throw new TypeError('MCP tool name is not compatible with provider tool names.');
203
+ return {
204
+ declaration: Object.freeze({ name: this.name, description: this.definition.description ?? '', inputSchema: this.definition.inputSchema }),
205
+ prepare: args => {
206
+ const input = this.inputParameters(args);
207
+ return async (invocation) => {
208
+ const message = await session.callTool(this.name, input, { signal: invocation.signal, returnsMessage: true });
209
+ if (message.parts.some(part => typeof part !== 'string'))
210
+ throw new UnsupportedCapabilityError('Agent MCP tools currently require text/structured output; direct calls preserve media.');
211
+ return message.metadata['structuredContent'] ?? message.text;
212
+ };
213
+ },
214
+ };
215
+ }
216
+ static resultToMessage(result, toolCallId = '') {
217
+ const parts = [];
218
+ for (const item of result.content) {
219
+ if (parts.length)
220
+ parts.push(' ');
221
+ if (item.type === 'text')
222
+ parts.push(item.text);
223
+ else if (item.type === 'image' || item.type === 'audio') {
224
+ const bytes = Uint8Array.from(atob(item.data), c => c.charCodeAt(0));
225
+ parts.push(item.type === 'image' ? Image.fromBytes(bytes, item.mimeType) : new Modality(item.mimeType, { kind: 'bytes', bytes }));
226
+ }
227
+ else
228
+ throw new UnsupportedCapabilityError(`Unsupported MCP result content: ${item.type}`);
229
+ }
230
+ return new ToolMessage(parts, toolCallId, { result: hasResult(result.structuredContent) ? result.structuredContent.result : undefined, metadata: { structuredContent: result.structuredContent, isError: result.isError ?? false } });
231
+ }
232
+ }
233
+ function freezeData(value) {
234
+ if (value && typeof value === 'object') {
235
+ for (const child of Object.values(value))
236
+ freezeData(child);
237
+ Object.freeze(value);
238
+ }
239
+ return value;
240
+ }
241
+ function hasResult(value) {
242
+ return value !== null && typeof value === 'object' && Object.hasOwn(value, 'result');
243
+ }
244
+ function assertJson(value, ancestors = new Set()) {
245
+ if (value === null || typeof value === 'string' || typeof value === 'boolean'
246
+ || typeof value === 'number' && Number.isFinite(value))
247
+ return;
248
+ if (typeof value !== 'object' || ancestors.has(value)
249
+ || !Array.isArray(value) && ![Object.prototype, null].includes(Object.getPrototypeOf(value))
250
+ || Object.getOwnPropertySymbols(value).length)
251
+ throw new ToolExecutionError('MCP arguments must be acyclic JSON data.');
252
+ ancestors.add(value);
253
+ for (const child of Array.isArray(value) ? value : Object.values(value))
254
+ assertJson(child, ancestors);
255
+ ancestors.delete(value);
256
+ }
257
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { McpClient } from './index.js';
2
+ export interface StdioMcpClientOptions {
3
+ readonly cwd?: string;
4
+ /** Explicit additions/overrides to the SDK's minimal inherited environment. */
5
+ readonly env?: Readonly<Record<string, string>>;
6
+ /** stdout is reserved for MCP; server diagnostics inherit stderr by default. */
7
+ readonly stderr?: 'inherit' | 'ignore';
8
+ readonly maxBufferSize?: number;
9
+ }
10
+ /** Node-only MCP client. Each session owns a fresh subprocess; no shell is used. */
11
+ export declare class StdioMcpClient extends McpClient {
12
+ constructor(command: string, args?: readonly string[], options?: StdioMcpClientOptions);
13
+ static fromCommand(command: string, args?: readonly string[], options?: StdioMcpClientOptions): StdioMcpClient;
14
+ }
@@ -0,0 +1,20 @@
1
+ import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
2
+ import { McpClient } from './index.js';
3
+ /** Node-only MCP client. Each session owns a fresh subprocess; no shell is used. */
4
+ export class StdioMcpClient extends McpClient {
5
+ constructor(command, args = [], options = {}) {
6
+ if (!command || command.includes('\0'))
7
+ throw new TypeError('Expected an executable command.');
8
+ if (options.maxBufferSize !== undefined && (!Number.isSafeInteger(options.maxBufferSize) || options.maxBufferSize <= 0)) {
9
+ throw new RangeError('maxBufferSize must be a positive safe integer.');
10
+ }
11
+ if (options.stderr !== undefined && !['inherit', 'ignore'].includes(options.stderr))
12
+ throw new TypeError('stderr must be inherit or ignore.');
13
+ const parameters = { ...options, command, args: [...args], ...(options.env ? { env: { ...options.env } } : {}) };
14
+ super({ transport: () => new StdioClientTransport({ ...parameters, args: [...parameters.args], ...(parameters.env ? { env: { ...parameters.env } } : {}) }) });
15
+ }
16
+ static fromCommand(command, args = [], options = {}) {
17
+ return new StdioMcpClient(command, args, options);
18
+ }
19
+ }
20
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/mcp/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAWvC,oFAAoF;AACpF,MAAM,OAAO,cAAe,SAAQ,SAAS;IAC3C,YAAY,OAAe,EAAE,IAAI,GAAsB,EAAE,EAAE,OAAO,GAA0B,EAAE;QAC5F,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,iCAAiC,CAAC,CAAC;QAC/F,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC,EAAE,CAAC;YACxH,MAAM,IAAI,UAAU,CAAC,gDAAgD,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAC;QAC9I,MAAM,UAAU,GAAG,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QACjH,KAAK,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,oBAAoB,CAAC,EAAE,GAAG,UAAU,EAAE,IAAI,EAAE,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACjK,CAAC;IACD,MAAM,CAAC,WAAW,CAAC,OAAe,EAAE,IAAI,GAAsB,EAAE,EAAE,OAAO,GAA0B,EAAE;QACnG,OAAO,IAAI,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;CACF"}
@@ -1,6 +1,6 @@
1
1
  # Implementation status: core runtime and three providers
2
2
 
3
- The implementation includes the M0 foundation, M1 tool loop, and substantial M2 functionality across Gemini, Anthropic, and OpenAI. Version `0.1.0-alpha.0` is prepared for an initial public alpha; publication is a separate step. The design documents remain the target scope; this page records what actually exists.
3
+ The implementation includes the M0 foundation, M1 tool loop, and substantial M2 functionality across Gemini, Anthropic, and OpenAI. Version `0.1.0-alpha.1` adds optional MCP HTTP and Node stdio integration. The design documents remain the target scope; this page records what actually exists.
4
4
 
5
5
  ## Implemented
6
6
 
@@ -48,7 +48,7 @@ The static example has been checked manually in Safari: both the page and a modu
48
48
 
49
49
  ## Not implemented yet
50
50
 
51
- Additional provider subclasses; Anthropic native-schema/thinking support; broader live-provider verification; full modality support beyond image descriptors; MCP; persistence; embeddings; evaluation; React bindings. See the porting plan for milestone ordering.
51
+ Additional provider subclasses; Anthropic native-schema/thinking support; broader live-provider verification; full modality support beyond image descriptors; persistence; embeddings; evaluation; React bindings. See the porting plan for milestone ordering.
52
52
 
53
53
  Invocation views are invalid after their action finishes. Cancellation stops queued work and rejects waiting calls, but user code/providers must observe the supplied AbortSignal to stop their underlying I/O. Synchronous application code cannot be forcibly interrupted. Stream cleanup likewise relies on cooperative provider generators. Trace results and metadata may refer to application-owned objects; snapshots freeze the trace structure, not arbitrary user values. Invocation IDs are unique within the loaded runtime; persistent/global IDs are a future storage concern.
54
54
 
@@ -75,3 +75,7 @@ A separate [feature evaluation](PYTHON_SCHEMA_EVALUATION.md) audits the schema i
75
75
  The rebuilt local demo was run in Chrome. Both the page and a real module Web Worker displayed PASS for the shared smoke suite, including `examples/python-protocol.ts`. The Python checks exercised default-protocol querying, registered union constructors, tuple/set values, UNKNOWN, character-chunked Unicode previews, final-only factory execution, and cancellation cleanup. Gemini/Anthropic/OpenAI integrations used mocked transports. No credentials or inference requests were used; this does not verify provider CORS, real-provider behavior, or all browsers.
76
76
 
77
77
  Repeat with `npm run serve:example`, then open the displayed localhost URL and check both PASS messages. The automated `npm run check:browser` remains a Web-API-only VM check, not a browser-engine test.
78
+
79
+ ## Optional MCP integration (0.1.0-alpha.1)
80
+
81
+ `langfx.js/mcp` adds McpClient, McpSession and runtime McpTool descriptors, using the official optional SDK for Streamable HTTP. Discovery, schema validation, result conversion, cancellation/disposal and explicitly selected Agent tools are implemented. Text/structured Agent results are supported; direct calls also retain image/audio content. The separate `langfx.js/mcp/node` entry adds StdioMcpClient.fromCommand with real subprocess lifecycle tests. Browser CSP and mocked protocol/package integration are tested; live-server OAuth/CORS remain unverified. See [MCP](MCP.md).
package/docs/MCP.md ADDED
@@ -0,0 +1,99 @@
1
+ # MCP clients, sessions and tools
2
+
3
+ Implemented in the optional `langfx.js/mcp` entry point, following Python Langfx's `McpClient`, `McpSession` and `McpTool` structure. Source audit: Python checkout `e33a5470d41e6cd45f8a1df1db555bf5d8bf9de7`, `langfx/mcp/{_client,_session,_tool}.py`. Included starting with `0.1.0-alpha.1`; `0.1.0-alpha.0` does not contain MCP support.
4
+
5
+ Install the optional `@modelcontextprotocol/client` peer (version 2.x; tested with 2.0.0). The core entry point neither loads nor requires the SDK. Import the MCP namespace separately:
6
+
7
+ ```ts
8
+ import * as lf from 'langfx.js';
9
+ import * as mcp from 'langfx.js/mcp';
10
+
11
+ const client = mcp.McpClient.fromUrl('https://your-server.example/mcp', {
12
+ // Host-owned authentication; SDK calls this for each request.
13
+ authProvider: { token: async () => getAccessToken() },
14
+ });
15
+ const tools = await client.listTools();
16
+ console.log(Object.keys(tools));
17
+
18
+ await client.withSession(async session => {
19
+ const result = await session.callTool('add', { x: 2, y: 3 });
20
+ // Equivalent descriptor call: tools.add.call({ x: 2, y: 3 }, session).
21
+ console.log(result);
22
+
23
+ const agent = new lf.Agent({
24
+ prompt: 'Add 2 and 3.',
25
+ tools: await session.agentTools(['add']), // Explicit names, never automatic exposure.
26
+ });
27
+ console.log(await agent.invoke(undefined, { lm })); // App-owned LanguageModel.
28
+ });
29
+ ```
30
+
31
+ The names and schemas above depend on your server. `fromUrl` supports `requestInit` (including headers), custom `fetch`, and the SDK's auth provider. Streamable HTTP is used explicitly; there is no URL-suffix inference or automatic legacy SSE fallback. The SDK owns initialization/version negotiation, JSON-RPC, HTTP/SSE parsing, request cancellation and authentication. This wrapper uses its default legacy negotiation mode for 2025-era MCP servers. See the [official SDK connection guide](https://ts.sdk.modelcontextprotocol.io/v2/clients/connect) and [protocol-version guide](https://ts.sdk.modelcontextprotocol.io/v2/protocol-versions).
32
+
33
+ ## API mapping
34
+
35
+ | Python | TypeScript |
36
+ | --- | --- |
37
+ | `McpClient.from_url` | `McpClient.fromUrl` |
38
+ | `client.list_tools(refresh=True)` | `await client.listTools({ refresh: true })` |
39
+ | `client.known_tools()` | `client.knownTools()` |
40
+ | `async with client.session()` | `client.withSession(async session => ...)` |
41
+ | Explicit context entry/exit | `await session.connect()` / `await session.close()` in `finally` |
42
+ | `session.list_tools()` | `await session.listTools()` |
43
+ | Generated `ToolClass(x=1).acall(session)` | `tools.name.call({ x: 1 }, session)` |
44
+ | `session.call_tool(name, returns_message=True, **args)` | `session.callTool(name, args, { returnsMessage: true })` |
45
+ | `McpTool.result_to_message` | `McpTool.resultToMessage` |
46
+
47
+ All network calls return promises; no `a`-prefixed duplicates or sync wrappers. Constructors and local input validation remain synchronous. Runtime descriptors replace generated Python symbolic classes and `McpToolInput`; there is no generated Python class definition for a discovered tool.
48
+
49
+ Client discovery caches immutable descriptors until explicitly refreshed. Session discovery fetches all pages, rejects duplicate names and updates its own descriptors. A fresh session discovers its current tool schemas before calling a tool, rather than touching private SDK caches. Descriptors can be reused with a fresh session; that session validates against its own current schema. Cached client discovery is not automatically invalidated by server notifications.
50
+
51
+ ## Validation and results
52
+
53
+ Inputs are JSON data, copied and validated locally before remote execution. Optional/defaulted fields are not inserted into arguments. Input and declared structured-output schemas use the SDK's `CfWorkerJsonSchemaValidator` explicitly, including on Node, to avoid dynamically generated validators in CSP-constrained hosts. Supported dialects and limitations follow [that validator](https://ts.sdk.modelcontextprotocol.io/v2/api/index/@modelcontextprotocol/client/); no remote reference fetching or schema-to-Python conversion is added. Unsupported dialects and unresolved references fail rather than disabling validation.
54
+
55
+ Default direct-call results follow Python's priority: an own `structuredContent.result` value, otherwise a `ToolMessage` for media, otherwise text joined with spaces. Unlike Python's truthiness check, `false`, `0`, empty string and null results are preserved. `returnsMessage: true` always returns a `ToolMessage`; all structured content is retained under `message.metadata.structuredContent`, and `result` is also available as `message.result`. Pass `toolCallId` when composing a correlated transcript manually.
56
+
57
+ Text, image and audio content are converted in order. Images use `Image`, audio uses generic `Modality`; the provider adapters still do not support audio inputs. Resource links/embedded resources explicitly reject. Protocol failures propagate; `isError` tool results throw `McpToolError`, which retains the raw response. No automatic retry is added to tool execution.
58
+
59
+ `session.agentTools(names)` adapts only the selected tools to the existing Agent contract. All batch arguments are validated before any execution. The agent adapter returns the complete structured content when available, otherwise text; Agent serializes that value as JSON. Image/audio results reject in the agent adapter because current provider tool-result paths require text. Direct MCP calls preserve media. Tool names must fit the existing provider naming rules; no silent renaming is performed.
60
+
61
+ ## Lifetime, cancellation and scope
62
+
63
+ Create each session once and close it in `finally`, or use `withSession`. `close` is idempotent, cancels pending operations, attempts HTTP session termination and always closes the SDK transport. Termination waiting is bounded to five seconds; a failed termination can leave remote cleanup to the server. Underlying custom fetch implementations must cooperate with cancellation. `withSession` preserves a callback failure even if cleanup also fails. Closed/failed sessions cannot reconnect; create a fresh session.
64
+
65
+ `connect`, `listTools`, and `callTool` accept `signal` and SDK request `timeout` in milliseconds. Cancellation is surfaced as Langfx `CancelledError`. Agent calls forward the invocation signal. The SDK may resume a transport stream internally; the wrapper does not retry a failed tool call. `withSession` options control connecting; pass signals to individual operations to cancel them during the callback.
66
+
67
+ For host-specific transports, construct `new McpClient({ transport: () => freshSdkTransport })`. The session owns that transport. This supports host bridges and testing without adding Node process APIs to the browser entry point. Stdio spawning is available through the separate Node entry point below. Python FastMCP embedding, legacy SSE selection, resources/prompts APIs, sampling, elicitation and task APIs are not ported.
68
+
69
+ Tests exercise the actual SDK with mocked Streamable HTTP, schema validation, discovery/cache/refresh, errors, media, cancellation, session cleanup and an Agent round trip. A browser bundle test disables dynamic string/wasm code generation. Packed-consumer checks cover optional dependency isolation. These are not live-server OAuth or browser CORS tests; the app/server must supply compatible CORS and authentication.
70
+
71
+ ## Node and desktop hosts: stdio
72
+
73
+ ```ts
74
+ import { StdioMcpClient } from 'langfx.js/mcp/node';
75
+
76
+ const client = StdioMcpClient.fromCommand('python', ['server.py'], {
77
+ cwd: '/path/to/server',
78
+ env: { SERVER_SETTING: 'value' },
79
+ stderr: 'inherit', // Default; 'ignore' is also supported.
80
+ });
81
+ await client.withSession(async session => {
82
+ console.log(Object.keys(await session.listTools()));
83
+ console.log(await session.callTool('add', { x: 2, y: 3 }));
84
+ });
85
+ ```
86
+
87
+ This entry point imports Node subprocess support; neither core nor `langfx.js/mcp` imports it. Use it in a Node process or desktop application's privileged host, not a browser renderer. The optional SDK peer is the same as for HTTP. `new StdioMcpClient(command, args, options)` is equivalent to `fromCommand`.
88
+
89
+ Each session spawns a fresh process with `shell: false`. Arguments are passed literally. Configure an executable and argument array; shell pipelines are not parsed. The SDK inherits a minimal platform-specific environment and overlays `env`; it does not copy the full parent environment. `cwd`, `env` and argument configuration are captured when the client is constructed. `maxBufferSize` optionally bounds the SDK's incoming message buffer (positive safe integer, bytes); its default is 10 MB. stdout is exclusively MCP JSON-RPC. Server diagnostics use inherited stderr by default or can be discarded with `stderr: 'ignore'`; raw piped stderr is not exposed by this convenience adapter.
90
+
91
+ Discovery via `client.listTools()` opens and closes a temporary child. Use one `withSession` callback for multiple calls sharing server state. A cancelled call sends SDK cancellation and leaves the session available; whether remote work stops depends on the server. Closing the session cancels pending requests and invokes the SDK's subprocess shutdown: close stdin, wait up to two seconds, then SIGTERM, wait up to two more seconds, then SIGKILL if necessary. Final OS reaping may follow that return. This manages the direct child, not an arbitrary descendant process tree. A failed/cancelled initialization also closes its owned transport.
92
+
93
+ FastMCP servers are supported as ordinary MCP peers: run a Python server with its stdio transport through this adapter, or connect to its HTTP endpoint with `McpClient.fromUrl`. No Python runtime is bundled, and there is no `fromFastMcp` API accepting Python objects. The host supplies the executable/environment. In-process TypeScript servers can instead use the existing fresh-transport factory and the SDK's linked in-memory transports.
94
+
95
+ Automated stdio tests use real Node child processes for handshake/discovery, literal arguments, environment/cwd, callback failures, early process exits, timeouts, request/initialization cancellation, and shutdown escalation. They require Node; the browser bundle test verifies this entry point is absent from its dependency graph.
96
+
97
+ A separate local smoke check passed against Python FastMCP from the existing Python checkout: discovery, `add(2, 3)` returning structured `5`, local rejection of invalid arguments, and child exit after session close. The repeatable server fixture is `tests/fixtures/mcp/fastmcp-server.py`; launch it with a Python executable that has `mcp` installed. Python is not a dependency of `npm run check`. This checks real FastMCP stdio interoperability, not HTTP CORS or OAuth.
98
+
99
+ The real localhost FastMCP Streamable HTTP check also passed discovery, structured results, invalid input rejection, the agent adapter and session closure. Repeat with `npm run build` followed by `node scripts/check-mcp-http.mjs /path/to/python-with-mcp`. The runner starts a temporary loopback server and stops it in cleanup. This does not verify browser CORS or remote OAuth.
@@ -52,7 +52,7 @@ P0 means required for the first useful release. P1 means a separate follow-up mi
52
52
  | Retry/concurrency helpers | P0 semantics | Abortable backoff, bounded queues, per-provider limits, run deadlines. Retry eligible model requests before visible stream output; no automatic replay after output or side effects. No thread pools or synchronous adapters. |
53
53
  | Fake models (`Echo`, static response/map/sequence) | P0 | Deterministic model and stream fixtures, including malformed output, tool calls, delays, errors, and cancellation. Keep testing utilities out of production imports. |
54
54
  | `mcp` client/session/tool generation | P1 optional module | Remote Streamable HTTP, discovery, explicit tool allowlist, structured/multimodal results, cancellation, and disposal. Runtime JSON Schema replaces dynamically generated Python tool classes. Use an SDK adapter rather than copying a protocol implementation. |
55
- | MCP stdio / in-process FastMCP | Omit from browser; possible host extension | Browser core cannot spawn a command or host a Python FastMCP object. Desktop/server hosts can supply an external bridge. Do not embed an MCP server into the core. |
55
+ | MCP stdio / in-process FastMCP | Optional Node stdio; omit Python embedding | `langfx.js/mcp/node` supplies StdioMcpClient.fromCommand. Core/browser imports cannot spawn commands. FastMCP servers connect through HTTP or stdio; Python objects are not embedded. |
56
56
  | `EmbeddingModel`, OpenAI/Vertex embeddings | P1 optional module | Small async embedding interface plus adapters. Useful for retrieval but unnecessary for basic agent orchestration; no vector database bundled. |
57
57
  | Evaluation examples, metrics, `ActionEval` | P1 subset | Lightweight async dataset runner, match/score callbacks, usage/timing, trace capture, JSON export. Explicit configuration matrices can replace symbolic sweeps. |
58
58
  | Evaluation Beam runners, checkpoint monitor, filesystem reports | Omit | Distributed experiment infrastructure and notebook/HTML reporting belong in the Python research toolchain or a future separate product. |
package/docs/RELEASING.md CHANGED
@@ -1,18 +1,20 @@
1
- # Initial alpha: 0.1.0-alpha.0
1
+ # MCP alpha: 0.1.0-alpha.1
2
2
 
3
- Release candidate for npm package `langfx.js`, published under the `alpha` dist-tag. The tag intentionally leaves `latest` untouched. Publication has not yet occurred.
3
+ Release target: `langfx.js@0.1.0-alpha.1` under the `alpha` dist-tag. The existing `latest` tag remains on `0.1.0-alpha.0`. Registry verification is required before declaring publication complete.
4
4
 
5
5
  ## Included
6
6
 
7
- Async TypeScript APIs for messages, templates, language functions, typed queries, structured streaming, tools, agents, and invocation traces. Python constructor syntax is the default structured-query protocol; JSON is explicit. Gemini, Anthropic, and OpenAI adapters support fixture-tested image inputs and local tool continuations. The package is ESM with TypeScript declarations, targets Node.js 22+ and browser/worker hosts, and has no required runtime dependencies. Zod is an optional peer.
7
+ Async TypeScript APIs for messages, templates, language functions, typed queries, structured streaming, tools, agents, and invocation traces. Python constructor syntax is the default structured-query protocol; JSON is explicit. Gemini, Anthropic, and OpenAI adapters support fixture-tested image inputs and local tool continuations. The package is ESM with TypeScript declarations, targets Node.js 22+ and browser/worker hosts, and has no required runtime dependencies. Zod is an optional peer. This release adds `langfx.js/mcp` for remote Streamable HTTP and `langfx.js/mcp/node` for local stdio servers, using an optional `@modelcontextprotocol/client` 2.x peer. It includes input/output validation, explicit agent tool selection, cancellation and subprocess cleanup. Unexpected MCP disconnects retain their failure diagnostics.
8
8
 
9
9
  ## Validation and limitations
10
10
 
11
11
  Run `npm ci` and `npm run check`. Checks cover runtime tests, type contracts, browser/worker Web-API realms, Python prompt/schema/streaming comparisons, isolated package imports/declarations, optional Zod integration, and the packaged README quick-start.
12
12
 
13
+ All 194 tests and full checks pass. Real local Python FastMCP stdio and Streamable HTTP smoke checks passed; remote MCP OAuth and browser CORS remain unverified.
14
+
13
15
  Gemini's seven-request live suite passed on 2026-09-16. OpenAI/Anthropic live inference and real-provider browser CORS remain unverified. Mocked browser/worker demos do not establish provider CORS support. Apps own authentication and transport; do not embed shared provider secrets in publicly delivered code.
14
16
 
15
- This is an alpha, with no stable API guarantee. Python schemas support a documented subset, and parsing does not evaluate generated code. Memory abstractions, persistence, MCP, embeddings, and React bindings are outside this release. See IMPLEMENTATION_STATUS.md and the Python parity reports for the precise scope.
17
+ This is an alpha, with no stable API guarantee. Python schemas support a documented subset, and parsing does not evaluate generated code. Memory abstractions, persistence, embeddings, and React bindings are outside this release. See IMPLEMENTATION_STATUS.md and the Python parity reports for the precise scope.
16
18
 
17
19
  ## Prepare and publish
18
20
 
@@ -20,6 +22,6 @@ This is an alpha, with no stable API guarantee. Python schemas support a documen
20
22
  2. Run `npm run check`. `npm pack --pack-destination <artifact-directory>` builds the declarations and JavaScript through `prepack`. Inspect the archive's file list and SHA-512 integrity. Only `dist`, docs, README, license/notice, and npm-required metadata belong in the archive.
21
23
  3. Review the exact archive with `npm publish <archive.tgz> --dry-run --tag alpha --access public --registry=https://registry.npmjs.org/`. Dry-run success does not establish publish rights or reserve the name.
22
24
  4. After publication approval, publish that reviewed archive with the same command without `--dry-run`. Directory publication additionally runs `prepublishOnly` to enforce `npm run check`; archive publication relies on the checks already completed for that archive. npm may require an interactive authentication challenge.
23
- 5. Verify `npm view langfx.js@0.1.0-alpha.0 version dist.integrity --json` matches the archive, and `npm dist-tag ls langfx.js` maps `alpha` to the released version. Install the exact version in a clean consumer and smoke-test it before marking the release complete.
25
+ 5. Verify `npm view langfx.js@0.1.0-alpha.1 version dist.integrity --json` matches the archive, and `npm dist-tag ls langfx.js` maps `alpha` to the released version. Install the exact version in a clean consumer and smoke-test it before marking the release complete.
24
26
 
25
27
  Do not move the `latest` tag as part of this alpha. Commit/tag the exact released source and update publication status after the registry verification succeeds. Never include npm credentials in the repository or archive.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langfx.js",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.1",
4
4
  "description": "Language as functions for TypeScript applications",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -40,6 +40,14 @@
40
40
  "./llms/openai": {
41
41
  "types": "./dist/llms/openai.d.ts",
42
42
  "import": "./dist/llms/openai.js"
43
+ },
44
+ "./mcp": {
45
+ "types": "./dist/mcp/index.d.ts",
46
+ "import": "./dist/mcp/index.js"
47
+ },
48
+ "./mcp/node": {
49
+ "types": "./dist/mcp/node.d.ts",
50
+ "import": "./dist/mcp/node.js"
43
51
  }
44
52
  },
45
53
  "scripts": {
@@ -63,17 +71,22 @@
63
71
  "prepublishOnly": "npm run check"
64
72
  },
65
73
  "devDependencies": {
74
+ "@modelcontextprotocol/client": "2.0.0",
66
75
  "@types/node": "^22.20.3",
67
76
  "esbuild": "^0.28.2",
68
77
  "typescript": "^7.0.2",
69
78
  "zod": "^4.6.5"
70
79
  },
71
80
  "peerDependencies": {
72
- "zod": "^4.0.0"
81
+ "zod": "^4.0.0",
82
+ "@modelcontextprotocol/client": "^2.0.0"
73
83
  },
74
84
  "peerDependenciesMeta": {
75
85
  "zod": {
76
86
  "optional": true
87
+ },
88
+ "@modelcontextprotocol/client": {
89
+ "optional": true
77
90
  }
78
91
  },
79
92
  "engines": {