Skip to content

Drive the Studio with DeepSeek Harness (preview)

Guide · Core Studio 3.0 · Verified 2026-09-01

Status: proven on 2026-08-31 (DS-2 legs, evidence in evidence/). A DeepSeek Harness (dsh) agent drives Core Studio’s metadata surface through the Studio’s MCP server — reads flow standalone, and every write passes through the Studio’s human approval gate, never dsh’s.

Every behavioral claim below is backed by a committed evidence leg (evidence/leg1-wiring … evidence/leg4-audit). Where a leg could not prove something, the text says so.


1. What you get

  • A dsh agent (web UI or headless) that can find, list, search, and read D365 F&O metadata in the Studio’s live local index, plus xref queries (references, closure, stats) — 12 read tools.
  • Four gated write tools (metadata_write, edit_apply, dotnet_build, dotnet_add_fo_reference) that pause on a human approval card in the running Studio desktop. No approval, no write — structurally.
  • A tamper-evident audit log of every call the agent makes.

2. Prerequisites

RequirementDetail
Core Studiorepo build or installed; the MCP server ships at packages/mcp-server/dist/bin.js (installed product: resources\mcp-server\run.cmd)
Pairing configgenerated by the desktop (Settings → Connect AI agents) → %APPDATA%\xpp-studio\mcp-server.json. The read tools need the index built (open the AOT root in the desktop once)
Node.js ≥ 22.13for the MCP server (node:sqlite)
dshnpx @deepseek-ai/dsh web — proven with 0.1.1-rc.2
dsh MCP client@deepseek-ai/[email protected] — first-party, opt-in
A provider keyany OpenAI-compatible endpoint dsh supports; the key lives in an environment variable, never in a config file (see §6)

3. The exact wiring (proven)

dsh consumes external MCP servers natively — no adapter, no shim. The bridge is a first-party plugin; the mount is one Cordis patch row.

  1. Install the plugin into your profile (once per profile):

    Terminal window
    dsh plugin --profile web add @deepseek-ai/[email protected]
    dsh plugin --profile headless add @deepseek-ai/[email protected]
  2. Add the mount to $DSH_HOME/cordis.patch.yml (home level → every profile) or to a single profile’s profiles/<name>/cordis.patch.yml:

    - insert:
    - id: mcp-studio
    name: '@deepseek-ai/dsh-mcp-client'
    config:
    serverName: studio # tools appear as mcp__studio__<tool>
    transport: stdio
    command: C:\Program Files\nodejs\node.exe
    args:
    - C:\xpp-studio\packages\mcp-server\dist\bin.js # or the installed run.cmd
    - '--config'
    - C:\Users\<you>\AppData\Roaming\xpp-studio\mcp-server.json
    toolCallTimeoutMs: 120000 # the approval card can pause a call; give the human room
    failOnStartupError: true
  3. Configure the model provider in $DSH_HOME/settings.yaml — the key is an env var reference, never a literal:

    llm-pi-ai:
    providers:
    my-gateway:
    apiKeyEnv: MY_PROVIDER_KEY # env var NAME only
    api: openai-completions
    baseURL: https://<your-openai-compatible-endpoint>/v1
    compat: # documented pi-ai switches for gateways
    supportsDeveloperRole: false # that reject the developer role and
    maxTokensField: max_tokens # max_completion_tokens
    models:
    - id: <your-model>
    agent-default-model:
    provider: my-gateway
    model: <your-model>
  4. Drive it: dsh web (browser UI) or dsh --profile headless "list the D365 models via mcp__studio__metadata_list_models".

Verified first contact (headless, one tool round):

{"count":3,"models":[{"name":"(root)"},{"name":"ApplicationFoundation"},{"name":"ApplicationSuite"}]}

4. The safety model — OUR gate, not theirs

dsh has its own permission layer (presets, an approval policy, a repeat-call guard). None of it is load-bearing for your metadata. The enforcement that matters lives in the Studio’s MCP server and desktop:

LayerWhat it doesProven by
Brokered approval gateWrite tools block on a human card in the running desktop (diff preview, 10-minute walk-away bound resolving as denial). There is no parameter, flag, or config that bypasses it from the agent sideleg 3b (denied → zero writes), leg 3c (granted → lands, audit ok)
Desktop-down refusalDesktop not running → immediate typed refusal, reads keep workingleg 3a (write_requires_desktop)
Scope confinementPaths must stay inside the pinned AOT root; .. segments and outside-root absolute paths are refused before any disk accessleg 3d (3/3 refused)
Rate limitPer-connection token bucket (60 burst, 1/s refill); exhaustion is a typed, retryable resultleg 3e (12 throttled of a 70-frame flood, exact text recorded)
Tamper-evident auditAppend-only hash-chained JSONL (prevHash/hash, fixed genesis); every tools/call recorded with verdict class; argument VALUES never logged (SHA-256 + byte size only)leg 4 (full log verifies clean; edit/delete/reorder all detected)

The write flow the agent experiences:

  1. tools/call metadata_write … → the call pauses while the desktop shows the approval card (that is why toolCallTimeoutMs should be generous).
  2. Human approves → the write lands via the never-drop writer + reindex; audit verdict ok.
  3. Human denies (or walks away) → isError: true, text write denied; the workspace is untouched.
  4. Desktop down → isError: true, text write_requires_desktop: metadata_write is brokered to CORE Enterprise Studio's approval gate — start Core Studio with this AOT root open and retry. Read tools work standalone.

Typed refusal inventory (verbatim, from the legs)

RefusalExact text (verbatim)
Desktop downwrite_requires_desktop: metadata_write is brokered to Core Studio's approval gate — start Core Studio with this AOT root open and retry. Read tools work standalone.
Denied at the gatewrite denied
Outside-root absolute pathmetadata_read failed: path outside the AOT root: C:/Windows/System32/drivers/etc/hosts (and the same for C:/AOSService/…)
Traversal segmentmetadata_read failed: invalid path: '..' is not allowed
Rate limitedrate_limited: xref_stats refused — this MCP connection exhausted its call budget; retry shortly (the bucket refills continuously)
Missing parent dir (granted write)metadata_write failed: ENOENT: no such file or directory, open '…\Ds2Scratch\AxClass\Ds2GateProbe.xml' — the writer requires a pre-existing model/module folder; nothing partial is written

5. What the legs proved, precisely

LegResultEvidence
1 Wiringdsh consumes external MCP natively via dsh-mcp-client; first-contact read succeeded on the first attemptevidence/leg1-wiring/
2 Read funnel (desktop UP)7 calls — list models, find elements, list elements, read, FTS source search, xref references, xref stats — every oracle matched the known sample estate (3 models; SampleClass→AxClass at its file; 5 ApplicationSuite elements by name; 25 xref edges for SalesFormLetter; schema v5 stats)evidence/leg2-reads/
3a Unapproved write, desktop DOWNtyped write_requires_desktop; audit verdict recordedevidence/leg3-gate/3a-desktop-down/
3b Write, approval DENIED (desktop UP)real card answered “no” through the desktop’s own consent IPC; write denied; target path never created; audit write_deniedevidence/leg3-gate/3b-desktop-up-denied/
3c Write, approval GRANTED (desktop UP)landed; read-back verified the exact element (type/name/methods); sha256 recorded; audit ok. (First attempt also proved the missing-folder ENOENT above.)evidence/leg3-gate/3c2-desktop-up-granted-lands/
3d Scope escape ×3all refused structurally; audit tool_error ×3evidence/leg3-gate/3d-scope-escape/
3e Rate limitagent-paced burst: dsh’s own repeat-call guard capped the agent at 20 identical calls — below the Studio’s 60-burst bucket, 0 throttled (recorded verbatim; the harness guard fires first at agent pace). Protocol-level flood on one raw connection: 58 ok + 12 throttled, typed text aboveevidence/leg3-gate/3e-rate-limit-*/
4 Audit trailthe session’s 43 calls all recorded with verdict classes (ok 34, tool_error 5, rate_limited 2, write_requires_desktop 1, write_denied 1); the server’s own verifyAuditChain verifies the full log clean; mid-log edit, deletion, and reorder each detected with precise line numbersevidence/leg4-audit/

The gate held in 100% of the hostile cases. No write ever landed unapproved.

6. Credential hygiene (do it this way)

  • The provider key is an environment variable for the session only (apiKeyEnv names it; the value never enters a config file, a log, or this repo). The DS-2 evidence redacts to fingerprint form (sk-kimi…).
  • The dsh stdio bridge scrubs credential-looking ambient variables before spawning the MCP server; the Studio server needs none.
  • The pairing config contains a machine-local pairing token — treat the file as private, never transmit it.

7. Honest limits

  • dsh is a developer preview. DeepSeek states compatibility-breaking changes are expected; the wiring above is pinned to dsh 0.1.1-rc.2 + dsh-mcp-client 0.1.1-rc.2. Re-verify after upgrades (the evidence/harness/run-leg.mjs runner re-runs any leg in one command).
  • Approval is human-paced. A write call blocks until the card is answered (10-minute desktop walk-away bound → denial). Set toolCallTimeoutMs accordingly; unattended “bulk write” agent runs are not a supported shape.
  • Writes need an existing folder. metadata_write does not create model/module directories — target an existing model folder (or create the structure through the desktop first).
  • The audit log is bounded, not complete under flood. Verdict records are batched through a capped queue; a burst faster than the disk is counted-dropped by design (availability outranks log completeness — the drop count makes gaps visible). At agent pace every call is recorded.
  • Rate limiting is per connection. Each dsh plugin instance (and each raw client) gets its own bucket.
  • Reads are only as fresh as the index. The read tools serve the desktop-built index of the pinned AOT root; switching roots requires regenerating the pairing config from the desktop.
  • One dsh-side behavior to know: its repeat-call guard stops identical rapid-fire tool bursts (~20) before the Studio’s bucket is ever reached — observed, recorded, and harmless to the safety story (both layers are fail-closed).
  • The MCP surface is metadata + xref + scaffold/compile only: no deploy, debug, test-run, OData, or environment-management tools — by design.

8. Reproducing the legs

All evidence tooling lives in evidence/harness/ (labeled evidence tooling, not product code): run-leg.mjs (one bounded headless dsh leg + session-log extraction with a fail-closed credential scrub), boot-desktop.mjs (dev desktop on the pairing root with CDP), approval-driver.mjs (answers the real approval card through the desktop’s own consent IPC), rate-flood.mjs (protocol-level throttle proof), audit-tamper.mjs (chain verification + tamper battery).