gemini-cli/packages/examples/background_agent/demo-background-agent.ts

218 lines
4.1 KiB
TypeScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const BackgroundAgentMessageSchema = z.object({
role: z.enum(['user', 'agent']),
parts: z.array(z.any()),
});
const BackgroundAgentTaskStatusSchema = z.object({
state: z.enum([
'submitted',
'working',
'input-required',
'completed',
'canceled',
'failed',
]),
message: BackgroundAgentMessageSchema.optional(),
});
const BackgroundAgentTaskSchema = z.object({
id: z.string(),
status: BackgroundAgentTaskStatusSchema,
history: z.array(BackgroundAgentMessageSchema).optional(),
});
type BackgroundAgentTask = z.infer<typeof BackgroundAgentTaskSchema>;
const server = new McpServer({
name: 'demo-background-agent',
version: '1.0.0',
});
const idToTask = new Map<string, BackgroundAgentTask>();
server.registerTool(
'startTask',
{
title: 'Start a new task',
description: 'Launches a new task asynchronously.',
inputSchema: { prompt: BackgroundAgentMessageSchema },
outputSchema: BackgroundAgentTaskSchema.shape,
},
({ prompt }) => {
const task: BackgroundAgentTask = {
id: crypto.randomUUID(),
status: {
state: 'submitted',
message: prompt,
},
history: [],
};
idToTask.set(task.id, task);
return {
content: [],
structuredContent: task,
};
},
);
server.registerTool(
'getTask',
{
title: 'Get a task',
inputSchema: { id: z.string() },
outputSchema: BackgroundAgentTaskSchema.shape,
},
({ id }) => {
const task = idToTask.get(id);
if (!task) {
return {
isError: true,
content: [
{
type: 'text',
text: 'No such task',
},
],
};
}
return {
content: [],
structuredContent: task,
};
},
);
server.registerTool(
'listTasks',
{
title: 'Lists tasks',
outputSchema: {
tasks: z.array(BackgroundAgentTaskSchema),
},
},
() => {
const out = {
tasks: Array.from(idToTask.values()),
};
return {
content: [],
structuredContent: out,
};
},
);
server.registerTool(
'messageTask',
{
title: 'Send a message to a task',
inputSchema: {
id: z.string(),
message: BackgroundAgentMessageSchema,
},
},
({ id, message }) => {
const task = idToTask.get(id);
if (!task) {
return {
isError: true,
content: [
{
type: 'text',
text: 'No such task',
},
],
};
}
task.history?.push(message);
task.status.message = message;
const statuses = BackgroundAgentTaskStatusSchema.shape.state.options;
const randomStatus = statuses[Math.floor(Math.random() * statuses.length)];
task.status.state = randomStatus;
return {
content: [],
};
},
);
server.registerTool(
'deleteTask',
{
title: 'Delete a task',
inputSchema: { id: z.string() },
},
({ id }) => {
const task = idToTask.get(id);
if (!task) {
return {
isError: true,
content: [
{
type: 'text',
text: 'No such task',
},
],
};
}
idToTask.delete(id);
return {
content: [
{
type: 'text',
text: 'Task deleted',
},
],
};
},
);
server.registerTool(
'cancelTask',
{
title: 'Cancels a task',
inputSchema: { id: z.string() },
},
({ id }) => {
const task = idToTask.get(id);
if (!task) {
return {
isError: true,
content: [
{
type: 'text',
text: 'No such task',
},
],
};
}
task.status.state = 'canceled';
return {
content: [
{
type: 'text',
text: 'Task cancelled',
},
],
};
},
);
const transport = new StdioServerTransport();
await server.connect(transport);