Demo background agent (#4409)
Co-authored-by: Bryan Morgan <bryanmorgan@google.com>
This commit is contained in:
parent
73745ecd03
commit
04bbc60b97
|
@ -916,6 +916,10 @@
|
||||||
"resolved": "packages/core",
|
"resolved": "packages/core",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@google/gemini-cli-examples": {
|
||||||
|
"resolved": "packages/examples",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/@google/genai": {
|
"node_modules/@google/genai": {
|
||||||
"version": "1.9.0",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.9.0.tgz",
|
||||||
|
@ -5657,6 +5661,19 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-tsconfig": {
|
||||||
|
"version": "4.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
|
||||||
|
"integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"resolve-pkg-maps": "^1.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "10.4.5",
|
"version": "10.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||||
|
@ -9190,6 +9207,16 @@
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/resolve-pkg-maps": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/restore-cursor": {
|
"node_modules/restore-cursor": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
|
||||||
|
@ -10490,6 +10517,26 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
|
"node_modules/tsx": {
|
||||||
|
"version": "4.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz",
|
||||||
|
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"esbuild": "~0.25.0",
|
||||||
|
"get-tsconfig": "^4.7.5"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"tsx": "dist/cli.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
|
@ -11860,6 +11907,17 @@
|
||||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"packages/examples": {
|
||||||
|
"name": "@google/gemini-cli-examples",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@modelcontextprotocol/sdk": "1.15.1",
|
||||||
|
"zod": "^3.23.8"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tsx": "^4.16.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"packages/vscode-ide-companion": {
|
"packages/vscode-ide-companion": {
|
||||||
"name": "gemini-cli-vscode-ide-companion",
|
"name": "gemini-cli-vscode-ide-companion",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Demo Background Agent
|
||||||
|
|
||||||
|
A pretend background agent that does not actually process tasks in the background. Configure in your settings.json with:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
"backgroundAgents": {
|
||||||
|
"demo-background-agent": {
|
||||||
|
"command": "npm",
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"start:demo-background-agent",
|
||||||
|
"--workspace=@google/gemini-cli-examples"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
|
@ -0,0 +1,217 @@
|
||||||
|
/**
|
||||||
|
* @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);
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "@google/gemini-cli-examples",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"start:demo-background-agent": "tsx background_agent/demo-background-agent.ts",
|
||||||
|
"build": "echo 'nothing to build'"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@modelcontextprotocol/sdk": "1.15.1",
|
||||||
|
"zod": "^3.23.8"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tsx": "^4.16.2"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue