DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

A2a — DSH Plugin for DeepSeek Harness
← Plugins

dsh-a2a

A2a

dsh-a2a: A2A v1.0 for DeepSeek Harness — expose the harness as an A2A agent and call remote A2A agents from agent tools.

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add dsh-a2a@0.7.1
READMECompatibilityVersions

Compatibility and provenance

A2a is published as dsh-a2a and currently resolves to version 0.7.1. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
npm
Registry updated
9/20/2026

Versions

0.7.1stable
8/31/2026
0.7.0stable
8/31/2026
0.5.0
stable
8/24/2026
Show 7 more versionsCollapse versions
0.4.2stable
8/21/2026
0.4.1stable
8/19/2026
0.4.0stable
8/19/2026
0.3.0stable
8/19/2026
0.2.1stable
8/18/2026
0.2.0stable
8/18/2026
0.1.0stable
8/15/2026

Related plugins

Loading related plugins…

Latest
0.7.1
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
430.1 kB
Files
9
Surface
web
License
MIT
Source
npm
GitHub
★ 1
Weekly downloads
75
Last push
8/31/2026
View source ↗Project homepage ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in

Related plugins

More verified plugins in agents-orchestration.

Headless@deepseek-ai/dsh-headlessThe dsh one-shot bundle: a direct core Agent/Session runner over dsh-base with no Host, HTTP, or browser layerExperimental Agent Team Web Profile@deepseek-ai/dsh-experimental-agent-team-web-profileExperimental Web profile layer for Agent Teams Remote and UI pluginsSubagent Codex@deepseek-ai/dsh-subagent-codexOne-shot Codex subagent provider over the official app-server protocolSubagent Claude Code@deepseek-ai/dsh-subagent-claude-codeOne-shot Claude Code subagent provider over the official Agent SDK

README

dsh-a2a

A2A v1.0 for DeepSeek Harness — expose the harness as an A2A agent, and let harness agents delegate to remote A2A agents.

One plugin, two halves, built on the official @a2a-js/sdk (A2A Protocol v1.0):

  • Server — a self-contained HTTP endpoint exposing the harness as an A2A agent: Agent Card discovery, JSON-RPC (the mandatory transport, SSE streaming included), and the HTTP+JSON REST surface. Every A2A contextId maps to a persistent harness session, so follow-up messages continue the same conversation.
  • Client — the a2a_call / a2a_list model tools, so harness agents can delegate work to remote A2A agents from a config-driven registry with header auth.

✨ Features

  • 🤖 Multi-agent server — one endpoint serves several agents, each at its own /agents/<id> with its own preset, model route, workspace group, Agent Card identity, and skills; one contextId maps to one persistent harness session per agent (deterministic sha256(contextId) ids), preset-mounted, persistent across turns.
  • 📇 Per-agent Agent Card skills — each agent advertises its abilities as A2A AgentSkill[] (id / name / description) on its own card, so an A2A client can route by what an agent is for.
  • 🎛️ Live served-agent management — the settings tab's inbound editor adds / removes / re-identifies a served agent; a save reconciles the running server without a restart.
  • 🔑 Token view + rotation — the settings tab shows the Bearer key (masked by default, reveal + copy) and rotates it live, persisting the new key to the settings document.
  • 🔌 Full A2A v1.0 wire — /.well-known/agent-card.json, JSON-RPC SendMessage / SendStreamingMessage / GetTask / CancelTask / ListTasks (SSE for streaming), REST message:send / tasks/* — all through the official SDK's request handler.
  • 🔒 Header auth on the client side — per-agent headers with ${ENV_VAR} placeholders resolved at call time; credentials never sit in the config.
  • 🔑 Bearer auth on the server side — with server.apiKey set, every request except the Agent Card must present Authorization: Bearer <key>; the card can also advertise a publicUrl behind a reverse proxy.
  • 🎛️ Per-request preset and model — a caller can name the preset and the model route on the A2A metadata map instead of using the deployment's route (see Per-request overrides).
  • 🧭 Model route and workspace — A2A conversations can pin a provider/model pair and live in their own workspace group (A2A, ~/.a2a-sessions by default).
  • ⏱️ Turn deadlines — a slow turn is cancelled (turnTimeoutMs, default 5 min) so the next message is never stuck.
  • 🛡️ Fail-loud config — bad URLs, empty names, or out-of-range ports throw at plugin load.
  • 🧪 Real-wire tests — the server half is exercised end to end with the official A2A client over a live HTTP port.

🚀 Quick Start

dsh plugin --profile web add dsh-a2a

export A2A_HOST=127.0.0.1
export A2A_PORT=8899

dsh web   # restart, then GET http://127.0.0.1:8899/.well-known/agent-card.json

Call the agent (JSON-RPC, blocking):

curl -s http://127.0.0.1:8899/ -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"role":"user","messageId":"m1","parts":[{"kind":"text","text":"hi"}],"contextId":"demo"}}}'

A2A_ENABLED=0 runs the client tools only (no listener).

⚙️ Configuration

The mounted row lives in ~/.dsh/profiles/web/cordis.patch.yml:

- id: a2a
  name: dsh-a2a
  config:
    server:
      enabled: true            # serve the A2A endpoint at all
      host: 127.0.0.1          # A2A_HOST
      port: 8899               # A2A_PORT
      turnTimeoutMs: 300000    # per-turn deadline
      allowOverrides: true     # let callers pick preset/model per request
      agents:                  # LOCAL agents served at /agents/<id>
        - id: support
          name: Support Agent
          description: Answers internal support questions.
          version: '0.1.0'
          preset: standard
          workspaceTitle: A2A
          skills:
            - id: query-orders
              name: Query orders
              description: Look up an order's status and timeline.
    agents: []                 # remote agents reachable from a2a_call

Each local agent lives at /agents/<id>/.well-known/agent-card.json and gets a session namespace (a2a-<id>-<hash>), its own workspace group, and its own Agent Card. The legacy single-agent form (server.preset / server.agentCard) still works: when server.agents is empty, one agent is derived from it.

FieldDefaultMeaning
server.enabledtrueServe the A2A endpoint; client tools work regardless
server.host / server.port127.0.0.1 / 8899Listen address (env A2A_HOST / A2A_PORT)
server.presetstandardPreset mounted into each A2A conversation agent
server.turnTimeoutMs300000Per-turn deadline; a slow turn is cancelled
server.allowOverridestrueWhether callers may pick preset/model per request via metadata; when false their overrides are dropped (and logged)
server.publicUrl—Public URL advertised on the Agent Card (required behind a reverse proxy); env A2A_PUBLIC_URL
server.apiKey—When set, every request except the Agent Card must present Authorization: Bearer <key>; env A2A_API_KEY
server.provider / server.model—Model route for A2A conversations, must be set as a pair; falls back to the harness default model; env A2A_PROVIDER / A2A_MODEL
server.cwd~/.a2a-sessionsWorking directory for A2A conversations (doubles as the sidebar workspace path); env DSH_A2A_CWD
server.workspaceTitleA2ASidebar group title for A2A conversations
server.agents[].id—URL slug under /agents/ (must be URL-safe), e.g. support
server.agents[].name / description / version—This agent's Agent Card identity
server.agents[].preset—Preset mounted into this agent's conversations

🎛️ Per-request overrides

By default every conversation uses the deployment's route: the configured server.preset and provider/model pair. A caller can pick its own instead by naming them on the A2A metadata map:

curl -s http://127.0.0.1:8899/ -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{
        "message":{"role":"user","messageId":"m1","parts":[{"kind":"text","text":"hi"}],"contextId":"demo"},
        "metadata":{"agentPreset":"general","model":"model-a"}
      }}'
KeyAliasesMeaningApplies
agentPresetpresetPreset id to compose the agent from, overriding server.preseton the request that creates the session
model—Model id, overriding server.modelon every request, and it sticks for the rest of the session
provider—Provider route, paired with the configured (or default) modelon every request

The two ride different lifetimes because the harness gives them different ones:

  • A preset composes an agent — it mounts tools and skills at session creation. Once a session exists its composition is fixed, so a preset on a later turn is ignored and logged: swapping tools mid-conversation would leave tool calls the new composition cannot make. Send the preset on the first message (or use a fresh contextId).
  • The model is a per-step route, so a live session is switched onto the requested model without losing the conversation — or the KV-cache prefix its history already earned.

Precedence and edge cases:

  • params.message.metadata wins over params.metadata; writing the same value to both — as the REST message:send shape invites — is unambiguous, and callers that only know one of them still work.
  • A bare model is paired with server.provider, falling back to the harness default model's provider: callers know the model they want, not the route serving it. Without any provider to pair with, the task fails rather than answering on a model the caller did not ask for.
  • A malformed value (a non-string, or one that could escape the preset root) fails the task with a message naming the key.
  • An unknown preset fails the task and lists the ids this deployment does supply.
  • Set server.allowOverrides: false to lock the deployment's route: overrides are then dropped and logged, and the request is still answered.

🏷️ Deployment customization (recommended)

dsh assemblies are layered: the bundle's own patch (npm-distributed) → the profile's cordis.patch.yml → environment variables. When giving a deployment its own identity, put each piece on the right layer:

  • Keep the shipped cordis.patch.yml generic — it reaches every npm consumer, so never bake deployment-specific identity (a bespoke preset, a dedicated Agent Card name/description) into it.
  • Deployment identity goes in the profile override layer — override the a2a row by id in ~/.dsh/profiles/web/cordis.patch.yml (no - insert: prefix, and spell out the full server block):
# ~/.dsh/profiles/web/cordis.patch.yml
- id: a2a
  name: dsh-a2a
  config:
    server:
      enabled: true
      host: 127.0.0.1
      port: 8899
      preset: my-support-preset      # deployment-specific preset
      turnTimeoutMs: 300000
      agentCard:
        name: My Support Agent       # deployment-specific identity
        description: Answers internal support questions.
        version: '0.1.0'
    agents: []
  • Secrets go in environment variables — the real values of publicUrl / apiKey / provider / model never sit in any YAML; keep the !!js process.env.XXX expressions and inject the values at launch (systemd users can use EnvironmentFile):
# /etc/dsh-web.env (systemd EnvironmentFile example)
A2A_PUBLIC_URL=https://gateway.example.com/
A2A_API_KEY=<secret>
A2A_PROVIDER=venus
A2A_MODEL=model-b

Patch-layer changes are read at assembly time — restart dsh web to apply them. The GUI registry (agents) is the exception: saves hot-reload.

💬 Model tools

ToolWhat it does
a2a_listLists the registered remote A2A agents (name + description).
a2a_callSends a prompt to one registered agent and returns its completed text reply.

🏗️ How it works

  • The server is a standalone Node HTTP listener (not a route on the web GUI): it serves several local agents, each at /agents/<id>/... with its own Agent Card, JSON-RPC + REST surface, preset-mounted executor, and session namespace. The root /.well-known/agent-card.json returns the first agent (there is no default agent at /); server.agents[] is the multi-agent form, and the legacy single-agent config derives one agent. Lifecycle is tied to the Cordis fiber — the listener starts on plugin load and closes (sockets included) on plugin disposal.
  • Each agent's executor implements the SDK's AgentExecutor contract: publish a task event, run one harness turn (followup → whenIdle with deadline → cancel on timeout), publish the reply as a message event, then a terminal statusUpdate. CancelTask reaches the right agent through the running-task table. The executor per agent is keyed by contextId (session namespace a2a-<agentId>-<hash>), so follow-up turns continue the same conversation.
  • The client resolves the registry through the SDK's ClientFactory (JSON-RPC + REST transports) with an authenticating fetch that injects the per-agent headers.

⚠️ Limitations

  • Inbound authentication: since 0.3.0 server.apiKey enforces a Bearer token on every request except the Agent Card (which must stay publicly readable); for larger deployments, still put the port behind an authenticating gateway or a reverse proxy. The Agent Card advertises no security schemes.
  • One executor instance serves every context of one agent (each /agents/<id> gets its own); sessions resume across turns but a dsh restart creates fresh in-memory state. The root /.well-known/agent-card.json returns the first agent only — address a specific agent at /agents/<id>/....
  • A model override switches sessions this executor instance created. One adopted from outside it — opened in the web UI, or resumed from disk — has no route handle to switch, so the override is logged and ignored; the reply keeps the model it was created with. Sending the model again after the executor creates the session works as expected.
  • Outbound registry edits require a profile patch + restart 0.2.0: GUI-configurable. The Plugins → Plugin configuration section ships an "A2A remote agents" card over the a2a settings namespace (schema defaults → row-config base → user overrides); a save hot-reloads the running tools, and a reset re-inherits the deployment registry.

🧪 Development

npm run check   # biome + typecheck + vitest (59 tests) + build

The suite covers config validation, the Agent Card + JSON-RPC round trip through the official A2A client against a real HTTP listener, per-context session continuity, task cancellation, header auth, per-request overrides, and the model-facing tools.

📄 License

MIT

server.agents[].provider / model—This agent's model route (must be a pair); falls back to the harness default
server.agents[].cwd / workspaceTitleserver.cwd / A2AThis agent's workspace directory / group title
server.agents[].skills[]A2A AgentSkill[] (id / name / description) advertised on this agent's card
server.agentCard.*—Legacy single-agent identity (used when server.agents is empty)
agents[].name / url—Remote registry name for a2a_call + Agent Card URL
agents[].headers{}Request headers; ${ENV_VAR} placeholders resolved at call time
agents[].description''Shown by a2a_list