Skip to content

-IN PROGRESS, EXPERIMENTAL- [GHCP] Add a typed-action shortcut to the plugin's MCP mode - #2973

Draft
George Ng (GeorgeNgMsft) wants to merge 1 commit into
mainfrom
georgengmsft-copilot-direct-actions
Draft

-IN PROGRESS, EXPERIMENTAL- [GHCP] Add a typed-action shortcut to the plugin's MCP mode#2973
George Ng (GeorgeNgMsft) wants to merge 1 commit into
mainfrom
georgengmsft-copilot-direct-actions

Conversation

@GeorgeNgMsft

@GeorgeNgMsft George Ng (GeorgeNgMsft) commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

What this does

If Copilot already knows exactly which TypeAgent action it wants to run, it can now run it — instead of writing an English sentence and asking TypeAgent to figure out what it meant.

Design doc, written for discussion rather than code review: ts/docs/plans/copilot-typed-actions/DESIGN.md

The problem

In MCP mode the plugin could only hand TypeAgent English. That's right when a human wrote the words. The problem shows up when nobody did.

Copilot is midway through a multi-step task and has assembled 20 songs from earlier results. It wants player.createPlaylist({ name, songs: [...] }) and already holds the exact structure. But the only door took English, so it had to write:

"create a playlist called Deep Focus with Kind of Blue by Miles Davis, Naima by John Coltrane, ..."

…and TypeAgent translated that back into the action Copilot started with. That costs a model call for a translation nobody needed, and twenty exact track and artist names have to survive being flattened into a sentence and parsed back out.

Translation exists to interpret human intent. When a machine composed the step, there's nothing to interpret — but the round trip can still lose data.

What's added

Tool Purpose
typeagent-processCommand Unchanged, still the default. Send English, TypeAgent translates
typeagent-executeAction Run one action directly by schemaName / actionName / parameters
typeagent-discoverActions Look up which actions exist and what parameters they take

The value is fidelity, determinism, and composability — structure the caller already holds reaches the dispatcher intact, schema validated, and TypeAgent actions become usable as steps inside a plan.

It is explicitly not faster for ordinary user requests. TypeAgent caches translations, so a phrase it has seen resolves with no model call at all — nothing here beats that. An earlier framing of this work claimed it avoided paying for translation on every request; that was wrong, and the scope is narrower as a result.

Which path a request takes

Not what the request doeswhere it came from. Words a human typed go to translation; structure Copilot already has goes direct.

Situation Path
User types "play some jazz music" processCommand
User types anything prefixed learn:, dev: or record: processCommand, always
Conversational, vague, or multi-step processCommand
User typed it and Copilot can't name the action processCommand
Copilot planned this step itself executeAction
Copilot already knows the action and parameters executeAction

The mistake to avoid: user request → discoverActionsexecuteAction. That's the most expensive route available, and the guidance shipped to the model says so explicitly. Discovery is for contracts reused across several calls.

What happens Copilot turns TypeAgent model calls
processCommand, phrase already known 1 0
processCommand, new phrase 1 1
executeAction, action already known 1 0
discoverActions then executeAction 2–3 0

It skips translation, not execution

@action runs the same executeActions engine an ordinary request does, so enabled-action gating, chained multi-step actions, result-entity resolution, memory recording, cancellation, and per-agent confirmation behave identically. Two documented differences: no prior-turn entity context (so "play it again" can't resolve — pass concrete parameters), and no errorReasoning retry (the error returns to the caller, which is the reasoner here). Neither tool can answer an agent's follow-up choice or form, so it reports the pending question instead of implying completion.

It also feeds the cache rather than starving it: on a genuine 1:1 match the user's words ride along as naturalLanguage and TypeAgent learns the phrasing.

Scaling

MCP's known trap is exposing N capabilities as N tools, putting all N descriptions in context on every turn. TypeAgent has ~418 actions and targets far more. This adds two tools, not one per action — the action space lives behind a tool, and discovery output is a tool result, present only when asked for. Discovery is tiered with deliberately no "list everything" call, and filtered to enabled schemas.

Neither tier is paginated; the agent list will outgrow it before any single agent's action list does. Discovery is live rather than snapshotted at handshake, because enablement changes mid-session and a stale catalog would advertise actions @action then refuses.

Fixes that stand on their own

  • Discovery could advertise actions @action would refuse — status active is true when either actions or commands are enabled, but execution gates on actions only. New optional actionActive reports the real thing, falling back to active for older agent servers.
  • commandExecutor filtered enabled schemas per top-level agent instead of per sub-schema.
  • commandExecutor mangled any naturalLanguage phrase containing an apostrophe.

Shared logic lives in @typeagent/dispatcher-types/helpers/actionDispatch rather than being copied. Also hardened: cancellation checked before output, aborted calls never submit side-effecting work, and names validated before reaching the command line.

Validation

  • pnpm run build dispatcher-types dispatcher copilot-plugin command-executor-mcp — succeeded
  • @typeagent/copilot-plugin: 12 suites, 85 tests passing; command-executor-mcp: 4 tests
  • Prettier applied; lint, complexity, circular-dependency, and tech-debt ratchets all pass with no regressions

Tests cover the MCP catalog exposing the tools from the real bundled server, discovery filtering by enabled schema, direct execution emitting the @action path rather than translation, injection rejection, cancellation, structured-content forwarding, and unchanged dev/bypass/processCommand behavior.

@GeorgeNgMsft
George Ng (GeorgeNgMsft) force-pushed the georgengmsft-copilot-direct-actions branch from 2c79952 to 6e77f00 Compare September 4, 2026 04:46
@GeorgeNgMsft
George Ng (GeorgeNgMsft) marked this pull request as draft September 4, 2026 05:10
@GeorgeNgMsft George Ng (GeorgeNgMsft) changed the title copilot-plugin: let Copilot run TypeAgent actions directly over MCP [GHCP] Let Copilot run TypeAgent actions directly over MCP Sep 4, 2026
@GeorgeNgMsft
George Ng (GeorgeNgMsft) force-pushed the georgengmsft-copilot-direct-actions branch 2 times, most recently from 490bc7e to 093bc68 Compare September 4, 2026 05:26
@GeorgeNgMsft George Ng (GeorgeNgMsft) changed the title [GHCP] Let Copilot run TypeAgent actions directly over MCP [GHCP] Add a typed-action shortcut to the plugin's MCP mode Sep 4, 2026
@GeorgeNgMsft
George Ng (GeorgeNgMsft) force-pushed the georgengmsft-copilot-direct-actions branch from 093bc68 to 69c33bb Compare September 4, 2026 05:34
@GeorgeNgMsft

Copy link
Copy Markdown
Contributor Author

I think this is an interesting idea that can improve reduce model calls and latency in certain scenarios. But not a good candidate for one-shotting. I may move this into a design doc to evaluate further before putting this out for review.

@GeorgeNgMsft
George Ng (GeorgeNgMsft) force-pushed the georgengmsft-copilot-direct-actions branch 3 times, most recently from a653061 to c8621be Compare September 4, 2026 06:13
In MCP mode the plugin could only send natural language to
typeagent-processCommand. That is the right default and stays the
default: TypeAgent caches translations, so a phrase it has seen
resolves with no model call at all, and Copilot's turn happens either
way. Nothing beats that for a user-authored request.

It is wasted work in one specific case. The MCP servers are registered
independently of the prompt hook, so their tools are in Copilot's
catalog on every turn of an agentic loop. When Copilot composed an
action itself as a step of a larger task there is no user phrasing to
translate, and processCommand forces it to synthesize prose describing
a structure it already holds so the dispatcher can parse it back into
that same structure - an extra model call, and a chance to mistranslate
something that was never ambiguous.

Add typeagent-discoverActions and typeagent-executeAction for that
case. executeAction runs the dispatcher's @action command, which uses
the same executeActions engine as an ordinary request, so enabled-action
gating, chained actions, entity resolution, memory recording,
cancellation and confirmation are unchanged; translation is what it
skips. Guidance tells the model not to discover a contract merely to
serve a request the user phrased, and to pass the user's words as
naturalLanguage on an exact match so the translation cache still learns
the phrasing.

The discovery and command-building protocol both MCP servers need now
lives in @typeagent/dispatcher-types/helpers/actionDispatch, and
command-executor uses it too. That fixes two bugs there: enabled
filtering now works per sub-schema instead of per top-level agent, and
a naturalLanguage phrase containing an apostrophe is no longer mangled
by the command tokenizer.

Also fixes discovery advertising actions that @action would refuse. The
dispatcher's status `active` is true when either an agent's actions or
its commands are enabled, but execution gates on actions alone; a new
optional actionActive field reports that, falling back to `active` for
older agent servers.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@GeorgeNgMsft
George Ng (GeorgeNgMsft) force-pushed the georgengmsft-copilot-direct-actions branch from c8621be to ed2f1b2 Compare September 4, 2026 06:14
@GeorgeNgMsft George Ng (GeorgeNgMsft) changed the title [GHCP] Add a typed-action shortcut to the plugin's MCP mode -IN PROGRESS, EXPERIMENTAL- [GHCP] Add a typed-action shortcut to the plugin's MCP mode Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant