Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@

### Changed

- Isolated invalid optional MCP tools while publishing Desktop Client Capability
manifests: malformed tools are omitted with diagnostics, complete manifest
budgets include services, and valid JSON-Schema MCP arguments are validated
before admission.
- Made typed `request()` the sole direct Runtime Host operation API; removed the 17 forwarding
aliases from direct and reconnecting connections while preserving status validation,
subscriptions, capabilities, listeners, lifecycle, and close behavior.
Expand Down
112 changes: 112 additions & 0 deletions apps/desktop/src/main/__tests__/runtime-host-client-uds.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,118 @@ test('drives Desktop Session operations through a real Runtime Host connection',
}
});

test('keeps the Desktop candidate usable when an optional MCP tool has an invalid schema', async () => {
const base = await mkdtemp(join(tmpdir(), 'maka-desktop-invalid-mcp-'));
let host: RuntimeHostKernel | undefined;
try {
const capability = await resolveStorageRoot({ path: base, kind: 'interactive' });
const owner = await tryAcquireInteractiveRootOwner(capability);
assert.ok(owner);
const projected = session('session-invalid-mcp');
host = await RuntimeHostKernel.start({
owner,
idleGraceMs: 10_000,
composition: defineInteractiveRuntimeHostComposition(async () => ({
handlers: handlers({
'client.capability.replace': async (input) => {
const mcp = input.offers.find((offer) => offer.offerId === 'desktop_mcp');
assert.deepEqual(mcp?.tools.map(({ name }) => name), ['mcp_valid']);
return {
ok: true,
result: { registrationId: input.registrationId, revision: 1 },
};
},
'client.capability.unregister': async (input) => ({
ok: true,
result: { registrationId: input.registrationId, revision: 2 },
}),
'session.catalog.query': async (input) => ({
ok: true,
result:
input.kind === 'get'
? { kind: 'session', session: input.sessionId === projected.id ? projected : null }
: {
kind: 'page',
revision: catalogRevision('1'),
sessions: [projected],
nextCursor: null,
},
}),
}),
beginDrain() {},
async recover() {},
async close() {},
})),
});
const ipc = ipcHarness();
const invalidTool = {
...nativeTool(),
name: 'mcp_invalid',
parameters: { jsonSchema: { type: 'string' } },
} as unknown as MakaTool;
const validTool = {
...nativeTool(),
name: 'mcp_valid',
parameters: { jsonSchema: { type: 'object', properties: {} } },
} as unknown as MakaTool;
const started = await startDesktopRuntimeHostCandidate({
rootPath: base,
candidateEntrypoint: new URL('file:///unused-runtime-host-candidate.js'),
ipcMain: ipc,
workspaceRoot: base,
attachmentApprovals: createAttachmentApprovalRegistry(),
stat: async () => ({ size: 0 }),
resizeImage: async (bytes) => bytes,
nativeCapabilities: {
browserTools: [],
resolveBrowserUrl: () => 'https://example.com/',
releaseBrowserSession() {},
computerUseTools: Object.assign([], {
clearSession() {},
}) as unknown as ComputerUseToolSet,
releaseComputerUseSession() {},
additionalGroups: () => [
{
offerId: 'desktop_mcp',
label: 'MCP',
description: 'MCP tools',
dynamic: true,
tools: [invalidTool, validTool],
},
],
},
botRegistry: {} as BotRegistry,
resolveBotCreateTarget: async () => ({
workspace: { kind: 'host_path', path: base },
}),
resolveSessionCreateProject: async () => ({ kind: 'host_path', path: base }),
emitSessionsChanged() {},
completeComputerUseTurn() {},
createSessionCopyCleanup: () => ({
ownCreation: (_creation, operation) => operation(),
rejectCreation: async () => undefined,
cleanup: async () => undefined,
schedule: async () => undefined,
abandonOwner: async () => undefined,
recover: async () => ({ removed: [], failed: [] }),
}),
});
assert.equal(started.kind, 'ready');
if (started.kind !== 'ready') throw new Error('Desktop candidate did not start');
const { candidate } = started;
ipc.setHost(candidate.client.hostId, ipc.epoch);

assert.deepEqual(
((await ipc.invoke('sessions:list')) as SessionCatalogProjection[]).map(({ id }) => id),
[projected.id],
);
await candidate.close();
} finally {
await host?.close().catch(() => undefined);
await rm(base, { recursive: true, force: true });
}
});

test('drives the renderer Session catalog facade through real UDS framing', async () => {
const base = await mkdtemp(join(tmpdir(), 'maka-desktop-host-ipc-'));
let host: RuntimeHostKernel | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import assert from 'node:assert/strict';
import test from 'node:test';
import { buildComputerUseTools, type ComputerUseToolSet } from '@maka/runtime/computer-use-tools';
import { type CuDispatchBackend } from '@maka/runtime/computer-use-types';
import { buildMcpTools, type McpToolProvider } from '@maka/runtime/mcp-tools';
import { type MakaTool, type MakaToolContext } from '@maka/runtime/tool-runtime';
import type { ClientCapabilityProvider } from '@maka/runtime-host/client';
import {
Expand Down Expand Up @@ -180,6 +181,222 @@ test('publishes every production Desktop-owned tool schema through the protocol'
);
});

test('publishes and invokes an MCP tool backed by an AI SDK JSON Schema', async () => {
let invocation: { args: Record<string, unknown>; cwd: string } | undefined;
let accepted = false;
const mcpProvider: McpToolProvider = {
toolSnapshot: () => ({
revision: 1,
tools: [
{
binding: 'fixture-binding' as never,
descriptor: {
serverId: 'fixture',
name: 'lookup',
inputSchema: {
type: 'object',
properties: { query: { type: 'string' } },
required: ['query'],
additionalProperties: false,
},
},
},
],
}),
async callTool(_binding, args, options) {
invocation = { args, cwd: options.context.cwd };
return { content: [{ type: 'text', text: 'found' }] };
},
};
const [mcpTool] = buildMcpTools(mcpProvider, { executionLocation: 'remote' });
assert.ok(mcpTool);
const provider = createDesktopNativeCapabilityProvider({
browserTools: [],
resolveBrowserUrl: () => 'https://example.com/',
releaseBrowserSession() {},
computerUseTools: computerTools(),
releaseComputerUseSession() {},
additionalGroups: () => [
{
offerId: 'desktop_mcp',
label: 'MCP',
description: 'MCP tools',
dynamic: true,
tools: [mcpTool],
},
],
});

assert.doesNotThrow(() =>
decodeClientCapabilityReplaceInput({
registrationId: 'registration-1',
offers: provider.offers(),
}),
);
await assert.rejects(
() =>
call(
provider,
capabilityFrame({
offerId: 'desktop_mcp',
serverId: 'desktop_mcp',
toolName: mcpTool.name,
arguments: {},
}),
() => {
accepted = true;
},
),
/Invalid arguments for tool/u,
);
assert.equal(accepted, false);
assert.equal(invocation, undefined);
assert.deepEqual(
await call(
provider,
capabilityFrame({
offerId: 'desktop_mcp',
serverId: 'desktop_mcp',
toolName: mcpTool.name,
arguments: { query: 'maka' },
}),
() => {
accepted = true;
},
),
{ content: [{ type: 'text', text: 'found' }] },
);
assert.deepEqual(invocation, {
args: { query: 'maka' },
cwd: '/workspace',
});
assert.equal(accepted, true);
});

test('chunks optional MCP tools that exceed one offer\'s tool limit', () => {
const diagnostics: string[] = [];
const provider = createDesktopNativeCapabilityProvider(
{
browserTools: [],
resolveBrowserUrl: () => 'https://example.com/',
releaseBrowserSession() {},
computerUseTools: computerTools(),
releaseComputerUseSession() {},
additionalGroups: () => [
{
offerId: 'desktop_mcp',
label: 'MCP',
description: 'MCP tools',
dynamic: true,
tools: Array.from({ length: 65 }, (_, index) =>
tool(`mcp_tool_${index + 1}`, z.object({}), async () => 'ok'),
),
},
],
},
{ onDiagnostic: (diagnostic) => diagnostics.push(diagnostic) },
);

assert.deepEqual(
provider.offers().map((offer) => [offer.offerId, offer.tools.length] as const),
[
['desktop_mcp', 64],
['desktop_mcp_2', 1],
],
);
assert.equal(diagnostics.length, 0);
assert.doesNotThrow(() =>
decodeClientCapabilityReplaceInput({ registrationId: 'registration-1', offers: provider.offers() }),
);
});

test('omits optional MCP tools that would exceed the complete manifest byte limit', () => {
const diagnostics: string[] = [];
const provider = createDesktopNativeCapabilityProvider(
{
browserTools: [],
resolveBrowserUrl: () => 'https://example.com/',
releaseBrowserSession() {},
computerUseTools: computerTools(),
releaseComputerUseSession() {},
additionalGroups: () => [
optionalMcpGroup('desktop_mcp_first', 'mcp_first', 28 * 1024),
optionalMcpGroup('desktop_mcp_second', 'mcp_second', 28 * 1024),
],
},
{ onDiagnostic: (diagnostic) => diagnostics.push(diagnostic) },
);

assert.deepEqual(provider.offers().map(({ offerId }) => offerId), ['desktop_mcp_first']);
assert.equal(diagnostics.length, 1);
assert.match(diagnostics[0] ?? '', /mcp_second/u);
assert.doesNotThrow(() =>
decodeClientCapabilityReplaceInput({ registrationId: 'registration-1', offers: provider.offers() }),
);
});

test('accounts for services when omitting optional MCP tools for the manifest budget', () => {
const diagnostics: string[] = [];
const offersOnlyProvider = createDesktopNativeCapabilityProvider({
browserTools: [],
resolveBrowserUrl: () => 'https://example.com/',
releaseBrowserSession() {},
computerUseTools: computerTools(),
releaseComputerUseSession() {},
additionalGroups: () => [
optionalMcpGroupWithTools('desktop_mcp', 'mcp_tool', 25 * 1024, 2),
],
});
const provider = createDesktopNativeCapabilityProvider(
{
browserTools: [],
resolveBrowserUrl: () => 'https://example.com/',
releaseBrowserSession() {},
computerUseTools: computerTools(),
releaseComputerUseSession() {},
additionalGroups: () => [
optionalMcpGroupWithTools('desktop_mcp', 'mcp_tool', 25 * 1024, 2),
],
additionalServices: () =>
Array.from({ length: 32 }, (_, index) => ({
serviceId: `service_${index}_${'x'.repeat(112)}`,
version: 'v'.repeat(64),
async call() {
return {};
},
})),
},
{
targetScope: { hostId: 'host-1', targetEpoch: 'epoch-1' },
onDiagnostic: (diagnostic) => diagnostics.push(diagnostic),
},
);

assert.equal(offersOnlyProvider.offers()[0]?.tools.length, 2);
assert.doesNotThrow(() =>
decodeClientCapabilityReplaceInput({
registrationId: 'registration-1',
offers: offersOnlyProvider.offers(),
}),
);
assert.throws(() =>
decodeClientCapabilityReplaceInput({
registrationId: 'registration-1',
offers: offersOnlyProvider.offers(),
services: provider.services?.(),
}), /manifest is too large/u);
assert.deepEqual(provider.offers()[0]?.tools.map(({ name }) => name), ['mcp_tool_1']);
assert.equal(diagnostics.length, 1);
assert.match(diagnostics[0] ?? '', /mcp_tool_2/u);
assert.doesNotThrow(() =>
decodeClientCapabilityReplaceInput({
registrationId: 'registration-1',
offers: provider.offers(),
services: provider.services?.(),
}),
);
});

test('publishes and admits additional Desktop native-effect services', async () => {
let admitted = false;
const provider = createDesktopNativeCapabilityProvider(
Expand Down Expand Up @@ -907,6 +1124,38 @@ function tool<P, R>(
};
}

function optionalMcpGroup(offerId: string, name: string, schemaDescriptionLength: number) {
return optionalMcpGroupWithTools(offerId, name, schemaDescriptionLength, 1);
}

function optionalMcpGroupWithTools(
offerId: string,
name: string,
schemaDescriptionLength: number,
toolCount: number,
) {
return {
offerId,
label: 'MCP',
description: 'MCP tools',
dynamic: true as const,
tools: Array.from({ length: toolCount }, (_, index) => ({
name: toolCount === 1 ? name : `${name}_${index + 1}`,
displayName: toolCount === 1 ? name : `${name}_${index + 1}`,
description: `${toolCount === 1 ? name : `${name}_${index + 1}`} description`,
parameters: {
jsonSchema: {
type: 'object',
description: 'x'.repeat(schemaDescriptionLength),
},
},
async impl() {
return 'ok';
},
}) as MakaTool),
};
}

function serviceFrame(): ClientCapabilityServiceCallFrame {
return {
kind: 'client.capability.service_call',
Expand Down
Loading