mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 12:54:07 -07:00
Revert "Update extension examples" (#16442)
This commit is contained in:
@@ -301,16 +301,6 @@ export default tseslint.config(
|
|||||||
'@typescript-eslint/no-require-imports': 'off',
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Examples should have access to standard globals like fetch
|
|
||||||
{
|
|
||||||
files: ['packages/cli/src/commands/extensions/examples/**/*.js'],
|
|
||||||
languageOptions: {
|
|
||||||
globals: {
|
|
||||||
...globals.node,
|
|
||||||
fetch: 'readonly',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// extra settings for scripts that we run directly with node
|
// extra settings for scripts that we run directly with node
|
||||||
{
|
{
|
||||||
files: ['packages/vscode-ide-companion/scripts/**/*.js'],
|
files: ['packages/vscode-ide-companion/scripts/**/*.js'],
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "hooks-example",
|
|
||||||
"version": "1.0.0"
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"hooks": {
|
|
||||||
"SessionStart": [
|
|
||||||
{
|
|
||||||
"hooks": [
|
|
||||||
{
|
|
||||||
"type": "command",
|
|
||||||
"command": "node ${extensionPath}/scripts/on-start.js"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright 2026 Google LLC
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
console.log(
|
|
||||||
'Session Started! This is running from a script in the hooks-example extension.',
|
|
||||||
);
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# MCP Server Example
|
|
||||||
|
|
||||||
This is a basic example of an MCP (Model Context Protocol) server used as a
|
|
||||||
Gemini CLI extension. It demonstrates how to expose tools and prompts to the
|
|
||||||
Gemini CLI.
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
The contents of this directory are a valid MCP server implementation using the
|
|
||||||
`@modelcontextprotocol/sdk`. It exposes:
|
|
||||||
|
|
||||||
- A tool `fetch_posts` that mock-fetches posts.
|
|
||||||
- A prompt `poem-writer`.
|
|
||||||
|
|
||||||
## Structure
|
|
||||||
|
|
||||||
- `example.js`: The main server entry point.
|
|
||||||
- `gemini-extension.json`: The configuration file that tells Gemini CLI how to
|
|
||||||
use this extension.
|
|
||||||
- `package.json`: Helper for dependencies.
|
|
||||||
|
|
||||||
## How to Use
|
|
||||||
|
|
||||||
1. Navigate to this directory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd packages/cli/src/commands/extensions/examples/mcp-server
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Install dependencies:
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
This example is typically used by `gemini extensions new`.
|
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||||
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
// Mock the MCP server and transport
|
||||||
|
const mockRegisterTool = vi.fn();
|
||||||
|
const mockRegisterPrompt = vi.fn();
|
||||||
|
const mockConnect = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('@modelcontextprotocol/sdk/server/mcp.js', () => ({
|
||||||
|
McpServer: vi.fn().mockImplementation(() => ({
|
||||||
|
registerTool: mockRegisterTool,
|
||||||
|
registerPrompt: mockRegisterPrompt,
|
||||||
|
connect: mockConnect,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@modelcontextprotocol/sdk/server/stdio.js', () => ({
|
||||||
|
StdioServerTransport: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('MCP Server Example', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
// Dynamically import the server setup after mocks are in place
|
||||||
|
await import('./example.js');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
vi.resetModules();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create an McpServer with the correct name and version', () => {
|
||||||
|
expect(McpServer).toHaveBeenCalledWith({
|
||||||
|
name: 'prompt-server',
|
||||||
|
version: '1.0.0',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should register the "fetch_posts" tool', () => {
|
||||||
|
expect(mockRegisterTool).toHaveBeenCalledWith(
|
||||||
|
'fetch_posts',
|
||||||
|
{
|
||||||
|
description: 'Fetches a list of posts from a public API.',
|
||||||
|
inputSchema: z.object({}).shape,
|
||||||
|
},
|
||||||
|
expect.any(Function),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should register the "poem-writer" prompt', () => {
|
||||||
|
expect(mockRegisterPrompt).toHaveBeenCalledWith(
|
||||||
|
'poem-writer',
|
||||||
|
{
|
||||||
|
title: 'Poem Writer',
|
||||||
|
description: 'Write a nice haiku',
|
||||||
|
argsSchema: expect.any(Object),
|
||||||
|
},
|
||||||
|
expect.any(Function),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should connect the server to an StdioServerTransport', () => {
|
||||||
|
expect(StdioServerTransport).toHaveBeenCalled();
|
||||||
|
expect(mockConnect).toHaveBeenCalledWith(expect.any(StdioServerTransport));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fetch_posts tool implementation', () => {
|
||||||
|
it('should fetch posts and return a formatted response', async () => {
|
||||||
|
const mockPosts = [
|
||||||
|
{ id: 1, title: 'Post 1' },
|
||||||
|
{ id: 2, title: 'Post 2' },
|
||||||
|
];
|
||||||
|
global.fetch = vi.fn().mockResolvedValue({
|
||||||
|
json: vi.fn().mockResolvedValue(mockPosts),
|
||||||
|
});
|
||||||
|
|
||||||
|
const toolFn = mockRegisterTool.mock.calls[0][2];
|
||||||
|
const result = await toolFn();
|
||||||
|
|
||||||
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
|
'https://jsonplaceholder.typicode.com/posts',
|
||||||
|
);
|
||||||
|
expect(result).toEqual({
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: JSON.stringify({ posts: mockPosts }),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('poem-writer prompt implementation', () => {
|
||||||
|
it('should generate a prompt with a title', () => {
|
||||||
|
const promptFn = mockRegisterPrompt.mock.calls[0][2];
|
||||||
|
const result = promptFn({ title: 'My Poem' });
|
||||||
|
expect(result).toEqual({
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: 'user',
|
||||||
|
content: {
|
||||||
|
type: 'text',
|
||||||
|
text: 'Write a haiku called My Poem. Note that a haiku is 5 syllables followed by 7 syllables followed by 5 syllables ',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a prompt with a title and mood', () => {
|
||||||
|
const promptFn = mockRegisterPrompt.mock.calls[0][2];
|
||||||
|
const result = promptFn({ title: 'My Poem', mood: 'sad' });
|
||||||
|
expect(result).toEqual({
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: 'user',
|
||||||
|
content: {
|
||||||
|
type: 'text',
|
||||||
|
text: 'Write a haiku with the mood sad called My Poem. Note that a haiku is 5 syllables followed by 7 syllables followed by 5 syllables ',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"nodeServer": {
|
"nodeServer": {
|
||||||
"command": "node",
|
"command": "node",
|
||||||
"args": ["${extensionPath}${/}example.js"],
|
"args": ["${extensionPath}${/}dist${/}example.js"],
|
||||||
"cwd": "${extensionPath}"
|
"cwd": "${extensionPath}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,13 @@
|
|||||||
"description": "Example MCP Server for Gemini CLI Extension",
|
"description": "Example MCP Server for Gemini CLI Extension",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "example.js",
|
"main": "example.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "~5.4.5",
|
||||||
|
"@types/node": "^20.11.25"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.23.0",
|
"@modelcontextprotocol/sdk": "^1.23.0",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"outDir": "./dist"
|
||||||
|
},
|
||||||
|
"include": ["example.ts"]
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "skills-example",
|
|
||||||
"version": "1.0.0"
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
name: greeter
|
|
||||||
description: A friendly greeter skill
|
|
||||||
---
|
|
||||||
|
|
||||||
You are a friendly greeter. When the user says "hello" or asks for a greeting,
|
|
||||||
you should reply with: "Greetings from the skills-example extension! 👋"
|
|
||||||
@@ -13,6 +13,6 @@
|
|||||||
"src/**/*.json",
|
"src/**/*.json",
|
||||||
"./package.json"
|
"./package.json"
|
||||||
],
|
],
|
||||||
"exclude": ["node_modules", "dist", "src/commands/extensions/examples"],
|
"exclude": ["node_modules", "dist"],
|
||||||
"references": [{ "path": "../core" }]
|
"references": [{ "path": "../core" }]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user