-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
Draft
-IN PROGRESS, EXPERIMENTAL- [GHCP] Add a typed-action shortcut to the plugin's MCP mode#2973George Ng (GeorgeNgMsft) wants to merge 1 commit into
George Ng (GeorgeNgMsft) wants to merge 1 commit into
Conversation
George Ng (GeorgeNgMsft)
force-pushed
the
georgengmsft-copilot-direct-actions
branch
from
September 4, 2026 04:46
2c79952 to
6e77f00
Compare
George Ng (GeorgeNgMsft)
marked this pull request as draft
September 4, 2026 05:10
George Ng (GeorgeNgMsft)
force-pushed
the
georgengmsft-copilot-direct-actions
branch
2 times, most recently
from
September 4, 2026 05:26
490bc7e to
093bc68
Compare
George Ng (GeorgeNgMsft)
force-pushed
the
georgengmsft-copilot-direct-actions
branch
from
September 4, 2026 05:34
093bc68 to
69c33bb
Compare
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. |
George Ng (GeorgeNgMsft)
force-pushed
the
georgengmsft-copilot-direct-actions
branch
3 times, most recently
from
September 4, 2026 06:13
a653061 to
c8621be
Compare
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>
George Ng (GeorgeNgMsft)
force-pushed
the
georgengmsft-copilot-direct-actions
branch
from
September 4, 2026 06:14
c8621be to
ed2f1b2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
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:…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
typeagent-processCommandtypeagent-executeActionschemaName/actionName/parameterstypeagent-discoverActionsThe 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 does — where it came from. Words a human typed go to translation; structure Copilot already has goes direct.
processCommandlearn:,dev:orrecord:processCommand, alwaysprocessCommandprocessCommandexecuteActionexecuteActionThe mistake to avoid: user request →
discoverActions→executeAction. 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.processCommand, phrase already knownprocessCommand, new phraseexecuteAction, action already knowndiscoverActionsthenexecuteActionIt skips translation, not execution
@actionruns the sameexecuteActionsengine 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 noerrorReasoningretry (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
naturalLanguageand 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
@actionthen refuses.Fixes that stand on their own
@actionwould refuse — statusactiveis true when either actions or commands are enabled, but execution gates on actions only. New optionalactionActivereports the real thing, falling back toactivefor older agent servers.commandExecutorfiltered enabled schemas per top-level agent instead of per sub-schema.commandExecutormangled anynaturalLanguagephrase containing an apostrophe.Shared logic lives in
@typeagent/dispatcher-types/helpers/actionDispatchrather 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 testsTests cover the MCP catalog exposing the tools from the real bundled server, discovery filtering by enabled schema, direct execution emitting the
@actionpath rather than translation, injection rejection, cancellation, structured-content forwarding, and unchanged dev/bypass/processCommand behavior.