MCP install targets
An MCP install target owns one client's Terminay MCP registration. Terminay supplies the server command; the target writes exactly that into the client's own configuration file.
Manifest contribution
JSON
"permissions": ["mcp-registration"],
"contributes": {
"mcpInstallTargets": [{
"id": "com.example.agent/example-agent",
"displayName": "Example Agent"
}]
} displayName is the client's name as Install Terminay MCP shows it.
A manifest may declare up to 16 targets.
Runtime contract
TypeScript
context.subscriptions.add(
context.mcp.registerInstallTarget(
'com.example.agent/example-agent',
exampleMcpInstallTarget,
),
);
The runtime implements status, install, and uninstall.
Each receives one request object.
| Request member | Meaning |
|---|---|
server.command, server.args, server.env? | The host-supplied launch command for the Terminay MCP server. Write exactly this and nothing else. |
signal | Aborts on the call's deadline or when the target is disposed. |
TypeScript
import { defineMcpInstallTarget } from '@terminay/extension-api';
const sameServer = (entry, server) =>
JSON.stringify(entry) ===
JSON.stringify({
command: server.command,
args: server.args,
env: server.env ?? {},
});
export const exampleMcpInstallTarget = defineMcpInstallTarget({
async status({ server }) {
try {
const entry = (await readConfig()).mcpServers?.terminay;
if (entry === undefined)
return { state: 'not-installed', configPath: configPath() };
return sameServer(entry, server)
? { state: 'installed', configPath: configPath() }
: {
state: 'changed',
configPath: configPath(),
message: 'The terminay entry points at a different command',
};
} catch {
return {
state: 'error',
configPath: configPath(),
message: 'The configuration file is not valid JSON',
};
}
},
async install({ server }) {
const config = await readConfig();
config.mcpServers = {
...config.mcpServers,
terminay: {
command: server.command,
args: server.args,
env: server.env ?? {},
},
};
await writeConfig(config); // Write a temporary file, then rename it.
return { ok: true, installed: true };
},
async uninstall() {
const config = await readConfig();
if (config.mcpServers?.terminay !== undefined) {
delete config.mcpServers.terminay;
await writeConfig(config);
}
return { ok: true, installed: false };
},
});Results
| Method | Returns |
|---|---|
status | state, configPath (the file the target inspects), and an optional message. |
install, uninstall | Booleans ok and installed, with optional message and error. |
| State | Meaning |
|---|---|
not-installed | The client has no Terminay entry. |
installed | The entry matches the supplied command. |
changed | An entry exists but differs from the supplied command. |
unavailable | The target cannot be used on this server. |
error | The configuration could not be read. |
Results are closed objects. message and error are at most 1,024
characters and configPath at most 4,096. When the server has no MCP server
command, Terminay reports every target unavailable and does not call the extension.