summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTommaso Sciortino <[email protected]>2025-07-18 15:21:46 -0700
committerGitHub <[email protected]>2025-07-18 22:21:46 +0000
commit04bbc60b97809b4200c5dd09ba849e4d097d1d1f (patch)
tree3b2646421eb4bb1f24080f21c6d5a6e37d678524
parent73745ecd0323882fc951e387250fe2efef374e81 (diff)
Demo background agent (#4409)
Co-authored-by: Bryan Morgan <[email protected]>
-rw-r--r--package-lock.json58
-rw-r--r--packages/examples/background_agent/README.md16
-rw-r--r--packages/examples/background_agent/demo-background-agent.ts217
-rw-r--r--packages/examples/package.json17
4 files changed, 308 insertions, 0 deletions
diff --git a/package-lock.json b/package-lock.json
index d5d0e294..947eb6ab 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -916,6 +916,10 @@
"resolved": "packages/core",
"link": true
},
+ "node_modules/@google/gemini-cli-examples": {
+ "resolved": "packages/examples",
+ "link": true
+ },
"node_modules/@google/genai": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.9.0.tgz",
@@ -5657,6 +5661,19 @@
"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": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
@@ -9190,6 +9207,16 @@
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
@@ -10490,6 +10517,26 @@
"dev": true,
"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": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -11860,6 +11907,17 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"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": {
"name": "gemini-cli-vscode-ide-companion",
"version": "0.0.1",
diff --git a/packages/examples/background_agent/README.md b/packages/examples/background_agent/README.md
new file mode 100644
index 00000000..0e5fd6dc
--- /dev/null
+++ b/packages/examples/background_agent/README.md
@@ -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"
+ ]
+ }
+ },
+```
diff --git a/packages/examples/background_agent/demo-background-agent.ts b/packages/examples/background_agent/demo-background-agent.ts
new file mode 100644
index 00000000..9ac568f4
--- /dev/null
+++ b/packages/examples/background_agent/demo-background-agent.ts
@@ -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);
diff --git a/packages/examples/package.json b/packages/examples/package.json
new file mode 100644
index 00000000..133d1b75
--- /dev/null
+++ b/packages/examples/package.json
@@ -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"
+ }
+}