Merge commit '70396085b141370ce32de1be4e225b4384eaf46d' into HEAD

# Conflicts:
#	.agents/notes/implemented/feature/2026-07-07-mcp-client-plugin.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-20-code-mode-typed-tool-returns.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-22-web-multimodal-image-input-and-durable-attachments.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-22-web-multimodal-image-input-and-durable-attachments.md
#	.agents/notes/implemented/feature/2026-07-22-web-multimodal-image-input-and-durable-attachments.zh.md
#	.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.i18n.yaml
#	docs/config-catalog.i18n.yaml
#	docs/module-graph.i18n.yaml
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	docs/tool-catalog.i18n.yaml
#	docs/tool-catalog.md
#	docs/tool-catalog.zh.md
#	examples/acp-agent/tests/snapshots/advanced-toolchain/tool-schemas.expected.json
#	examples/acp-agent/tests/snapshots/both-mode-turn/tool-schemas.expected.json
#	examples/acp-agent/tests/snapshots/code-mode-turn/tool-schemas.expected.json
#	packages/core/tools/README.i18n.yaml
#	packages/core/tools/README.zh.md
#	packages/core/tools/src/code-mode.ts
#	packages/host/apiproxy/tests/api-proxy-models.spec.ts
#	packages/host/plugin-inventory/tests/inventory.spec.ts
#	packages/mcp/mcp-client/tests/mcp-client.e2e.ts
#	packages/mcp/mcp-client/tests/mcp-client.spec.ts
#	packages/self-modification/tool-cordis/src/api-catalog.ts
#	packages/test-support/acp-snapshot/README.i18n.yaml
#	pnpm-lock.yaml
This commit is contained in:
Tianyi Cui
2026-08-17 11:31:59 +08:00
3845 changed files with 62208 additions and 100402 deletions

View File

@@ -1,9 +1,9 @@
{
"name": "@deepseek-ai/dsh-mcp-client",
"description": "MCP client bridge: connects to MCP servers and registers their tools on ctx.tools",
"version": "0.0.1-rc.2",
"version": "0.1.0-rc.6",
"publishConfig": {
"access": "restricted"
"access": "public"
},
"repository": {
"type": "git",
@@ -30,7 +30,7 @@
"lib/invariant.js",
"lib/types/**/*.d.ts"
],
"license": "BSD-3-Clause",
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-attachment": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",

View File

@@ -1,5 +1,5 @@
/**
* Tool bridge: discovers MCP tools, registers them on the harness ToolRegistry
* Tool bridge: discovers MCP tools, registers them on the harness ToolRuntime
* under deterministic server-qualified public names, and handles re-sync when
* the server's tool list changes.
*
@@ -106,7 +106,7 @@ function callToolUncached(
*
* @param serverName - Stable local namespace from plugin config.
* @param rawName - The MCP server's own tool name.
* @returns The globally unique, model-facing ToolRegistry name.
* @returns The globally unique, model-facing ToolRuntime name.
*/
export function publicToolName(serverName: string, rawName: string): string {
const joined = `mcp__${serverName}__${rawName}`
@@ -117,7 +117,7 @@ export function publicToolName(serverName: string, rawName: string): string {
}
/**
* Sync the MCP server's tool list into the harness ToolRegistry.
* Sync the MCP server's tool list into the harness ToolRuntime.
*
* Two phases keep the swap safe:
*
@@ -207,7 +207,7 @@ interface McpContentBlock {
uri?: string
}
/** Async rich projection staged for one exact ToolRegistry execution. */
/** Async rich projection staged for one exact ToolRuntime execution. */
interface PreparedProjection {
/** Canonical MCP value returned by execute before registry materialization. */
value: McpResult
@@ -239,7 +239,7 @@ function supportedOutputSchema(candidate: unknown): JsonSchemaNode | undefined {
* @param structuredSchema - supported structured-output schema, when advertised.
* @param taskRequired - whether this MCP tool requires unsupported task execution.
* @param opts - bridge timeout and namespace options.
* @returns a complete ToolRegistry definition.
* @returns a complete ToolRuntime definition.
*/
function createDefinition(
client: Client,
@@ -298,7 +298,7 @@ function createOutput(rawName: string, structuredSchema: JsonSchemaNode | undefi
* per-page schema cache from pre-validating a different contract.
*
* When the MCP server returns `isError: true`, the executor throws so that
* the ToolRegistry's catch path produces an `isError` result for the model.
* the ToolRuntime's catch path produces an `isError` result for the model.
*/
function createExecutor(
client: Client,
@@ -340,7 +340,7 @@ function createExecutor(
const content = result.content as unknown as JsonValue[]
const text = extractText(content, rawName)
// MCP isError → throw so ToolRegistry produces an isError result for the model.
// MCP isError → throw so ToolRuntime produces an isError result for the model.
if (result.isError === true) {
throw new Error(text)
}

View File

@@ -5,7 +5,7 @@
import { describe, expect, it, vi, beforeEach } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry from '@deepseek-ai/dsh-tools'
import ToolRuntime from '@deepseek-ai/dsh-tools'
import type { Config } from '@deepseek-ai/dsh-mcp-client'
// ---- Mock MCP SDK ----
@@ -61,7 +61,7 @@ import { apply, name, inject, Config as ConfigSchema } from '@deepseek-ai/dsh-mc
async function mountRegistry(): Promise<Context> {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(ToolRuntime)
return ctx
}
@@ -211,7 +211,7 @@ describe('apply (plugin lifecycle)', () => {
it('releases the serverName reservation on dispose', async () => {
const first = new Context()
await first.plugin(SystemPrompt)
await first.plugin(ToolRegistry)
await first.plugin(ToolRuntime)
await apply(first, stdioConfig)
await first.fiber.dispose()
@@ -221,7 +221,7 @@ describe('apply (plugin lifecycle)', () => {
// and the disposed instance no longer holds the reservation on its root.
const second = new Context()
await second.plugin(SystemPrompt)
await second.plugin(ToolRegistry)
await second.plugin(ToolRuntime)
await expect(apply(second, stdioConfig)).resolves.toBeUndefined()
await second.fiber.dispose()
})

View File

@@ -21,8 +21,8 @@ import { z } from 'zod'
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'
import LocalAttachmentStore from '@deepseek-ai/dsh-attachment-local'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry from '@deepseek-ai/dsh-tools'
import { CallId, LlmAdapter, LlmService } from '@deepseek-ai/dsh-llm'
import ToolRuntime from '@deepseek-ai/dsh-tools'
import { CallId, LlmAdapter, LlmRuntime } from '@deepseek-ai/dsh-llm'
import type { GenerateOptions, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm'
import { apply } from '@deepseek-ai/dsh-mcp-client/src/index.ts'
import { publicToolName } from '@deepseek-ai/dsh-mcp-client/src/tools.ts'
@@ -41,7 +41,7 @@ const localBin = join(packageDir, 'node_modules', '.bin')
async function mountRegistry(): Promise<Context> {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(ToolRuntime)
return ctx
}
@@ -59,7 +59,7 @@ class ImageAdapter extends LlmAdapter {
async function mountImageRegistry(dshHome: string): Promise<Context> {
const ctx = await mountRegistry()
await ctx.plugin(LocalAttachmentStore, { dshHome })
await ctx.plugin(LlmService)
await ctx.plugin(LlmRuntime)
ctx.llm.registerAdapter(['visual'], new ImageAdapter())
return ctx
}

View File

@@ -4,11 +4,11 @@ import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
import { Context } from '@deepseek-ai/cordis'
import AttachmentStore, { AttachmentError, AttachmentId } from '@deepseek-ai/dsh-attachment'
import type { ImageAttachmentLimits, ImageAttachmentRef, SaveImageAttachment, StoredImageAttachment } from '@deepseek-ai/dsh-attachment'
import { CallId, LlmAdapter, LlmService } from '@deepseek-ai/dsh-llm'
import { CallId, LlmAdapter, LlmRuntime } from '@deepseek-ai/dsh-llm'
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { GenerateOptions, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry, { type JsonValue } from '@deepseek-ai/dsh-tools'
import ToolRuntime, { type JsonValue } from '@deepseek-ai/dsh-tools'
import type { PostToolDecision } from '@deepseek-ai/dsh-tools'
import { publicToolName, syncTools, type ToolBridgeOptions } from '@deepseek-ai/dsh-mcp-client/src/tools.ts'
import { createTransport } from '@deepseek-ai/dsh-mcp-client/src/transport.ts'
@@ -64,7 +64,7 @@ function createMockClient(tools: MockTool[], callResult: MockCallResult = { cont
async function mountRegistry(): Promise<Context> {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(ToolRuntime)
return ctx
}
@@ -121,7 +121,7 @@ class ImageCatalogAdapter extends LlmAdapter {
async function mountRichRegistry(): Promise<{ ctx: Context; attachments: RecordingAttachmentStore }> {
const ctx = await mountRegistry()
await ctx.plugin(RecordingAttachmentStore)
await ctx.plugin(LlmService)
await ctx.plugin(LlmRuntime)
ctx.llm.registerAdapter(['visual'], new ImageCatalogAdapter())
return { ctx, attachments: ctx.attachments as RecordingAttachmentStore }
}

View File

@@ -7,7 +7,7 @@
import { describe, expect, it, vi, beforeEach } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry from '@deepseek-ai/dsh-tools'
import ToolRuntime from '@deepseek-ai/dsh-tools'
import { CallId } from '@deepseek-ai/dsh-llm'
import type { Config } from '@deepseek-ai/dsh-mcp-client'
@@ -68,7 +68,7 @@ const testToolSignal = new AbortController().signal
async function mountRegistry(): Promise<Context> {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(ToolRuntime)
return ctx
}

View File

@@ -25,7 +25,7 @@
"path": "../../subprocess/subprocess"
},
{
"path": "../../support/invariants"
"path": "../../runtime-diagnostics/invariants"
},
{
"path": "../../util/timeout"