Skip to content

Commit df93523

Browse files
v0.0.5: Add Prompt Generator, API Wiki, sidebar quick actions, VS Code tools, and professional README
Features: - Prompt Generator: AI-powered tool to create detailed prompts with dropdowns for project type, tech stack, goal, and complexity - API Usage Wiki: Comprehensive code examples for Python, JavaScript, cURL, and MCP tools - Sidebar Quick Actions: Swagger, Wiki, and Prompt Generator buttons - Built-in VS Code Tools: vscode_read_file, vscode_list_files, vscode_open_file, vscode_get_diagnostics, vscode_get_active_editor - Status bar improvements with active request indicator - Auto-start and notification settings Documentation: - Professional README rewrite with SEO optimization - Framework integrations: LangChain, LlamaIndex, AutoGPT, CrewAI, etc. - Comprehensive configuration reference - Built-in VS Code tools documentation
1 parent 2d9700e commit df93523

11 files changed

Lines changed: 38734 additions & 421 deletions

README.md

Lines changed: 315 additions & 156 deletions
Large diffs are not rendered by default.

dist/extension.js

Lines changed: 37300 additions & 205 deletions
Large diffs are not rendered by default.

dist/extension.js.map

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
-45.9 KB
Binary file not shown.
-699 KB
Binary file not shown.

package.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"displayName": "GitHub Copilot API Gateway",
44
"description": "Expose GitHub Copilot as a local OpenAI-compatible API server. Use any OpenAI SDK, curl, or HTTP client to access Copilot's language models.",
55
"icon": "media/icon.png",
6-
"version": "0.0.3",
6+
"version": "0.0.5",
77
"author": {
88
"name": "Suhaib Bin Younis",
99
"email": "vscode@suhaib.in",
@@ -139,6 +139,16 @@
139139
"default": true,
140140
"description": "Expose the WebSocket realtime endpoint at /v1/realtime."
141141
},
142+
"githubCopilotApi.server.autoStart": {
143+
"type": "boolean",
144+
"default": false,
145+
"description": "Automatically start the Copilot API server when VS Code starts."
146+
},
147+
"githubCopilotApi.server.showNotifications": {
148+
"type": "boolean",
149+
"default": true,
150+
"description": "Show system notifications when the server starts or stops."
151+
},
142152
"githubCopilotApi.server.port": {
143153
"type": "number",
144154
"default": 3030,
@@ -233,4 +243,4 @@
233243
}
234244
}
235245
}
236-
}
246+
}

src/CopilotApiGateway.ts

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,8 @@ export class CopilotApiGateway implements vscode.Disposable {
324324
uptimeMs: Date.now() - this.usageStats.startTime,
325325
avgLatencyMs: this.realtimeStats.avgLatencyMs,
326326
requestsPerMinute: this.realtimeStats.requestsPerMinute,
327-
errorRate: this.realtimeStats.errorRate
327+
errorRate: this.realtimeStats.errorRate,
328+
mcp: this.getMcpStatus()
328329
};
329330
}
330331

@@ -816,23 +817,31 @@ export class CopilotApiGateway implements vscode.Disposable {
816817
const requestStart = Date.now();
817818
const requestId = randomUUID().slice(0, 8);
818819

819-
void this.handleHttpRequest(req, res, requestId, requestStart).catch(error => {
820-
const duration = Date.now() - requestStart;
821-
if (error instanceof ApiError) {
822-
this.logRequest(requestId, req.method || 'UNKNOWN', req.url || '/', error.status, duration, {
823-
error: error.message,
824-
requestHeaders: req.headers
825-
});
826-
this.sendError(res, error);
827-
} else {
828-
this.logRequest(requestId, req.method || 'UNKNOWN', req.url || '/', 500, duration, {
829-
error: error instanceof Error ? error.message : String(error),
830-
requestHeaders: req.headers
831-
});
832-
this.logError('Unhandled error in HTTP request handler', error);
833-
this.sendError(res, new ApiError(500, 'An unexpected error occurred.', 'server_error'));
834-
}
835-
});
820+
void this.handleHttpRequest(req, res, requestId, requestStart)
821+
.catch(error => {
822+
const duration = Date.now() - requestStart;
823+
if (error instanceof ApiError) {
824+
this.logRequest(requestId, req.method || 'UNKNOWN', req.url || '/', error.status, duration, {
825+
error: error.message,
826+
requestHeaders: req.headers
827+
});
828+
this.sendError(res, error);
829+
} else {
830+
this.logRequest(requestId, req.method || 'UNKNOWN', req.url || '/', 500, duration, {
831+
error: error instanceof Error ? error.message : String(error),
832+
requestHeaders: req.headers
833+
});
834+
this.logError('Unhandled error in HTTP request handler', error);
835+
this.sendError(res, new ApiError(500, 'An unexpected error occurred.', 'server_error'));
836+
}
837+
})
838+
.finally(() => {
839+
this.activeRequests--;
840+
this._onDidChangeStatus.fire();
841+
});
842+
843+
this.activeRequests++;
844+
this._onDidChangeStatus.fire();
836845
});
837846

838847
this.httpServer.on('error', error => {
@@ -2456,7 +2465,7 @@ export class CopilotApiGateway implements vscode.Disposable {
24562465
}
24572466
}
24582467

2459-
private async invokeCopilot(prompt: string): Promise<string> {
2468+
public async invokeCopilot(prompt: string): Promise<string> {
24602469
// Use the VS Code Language Model API to invoke Copilot programmatically
24612470
// This does NOT open the chat window
24622471
const models = await vscode.lm.selectChatModels({ vendor: 'copilot' });

src/CopilotPanel.ts

Lines changed: 811 additions & 20 deletions
Large diffs are not rendered by default.

src/McpService.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import * as vscode from 'vscode';
22
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
33
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
44
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
5+
import { VSCodeToolProvider } from './services/VSCodeToolProvider';
56
import {
67
ListToolsResultSchema,
78
CallToolResultSchema,
8-
CompatibilityCallToolResult
9+
CallToolResult
910
} from '@modelcontextprotocol/sdk/types.js';
1011

1112
export interface McpServerConfig {
@@ -26,8 +27,11 @@ export class McpService implements vscode.Disposable {
2627
private clients: Map<string, Client> = new Map();
2728
private cachedTools: McpTool[] = [];
2829
private disposables: vscode.Disposable[] = [];
30+
private vscodeTools: VSCodeToolProvider;
2931

30-
constructor(private readonly output: vscode.OutputChannel) { }
32+
constructor(private readonly output: vscode.OutputChannel) {
33+
this.vscodeTools = new VSCodeToolProvider();
34+
}
3135

3236
public async initialize(): Promise<void> {
3337
await this.refreshServers();
@@ -61,6 +65,14 @@ export class McpService implements vscode.Disposable {
6165
}
6266

6367
public getTools(): McpTool[] {
68+
// Always include builtin tools, even if cache is empty or stale.
69+
// We check if cachedTools is empty to avoid duplication if getAllTools already merged them,
70+
// although getAllTools implementation below does merge them.
71+
// The issue is if refreshServers() was never called or failed, cachedTools might be empty,
72+
// but we still want the VSCode tools to be available.
73+
if (this.cachedTools.length === 0) {
74+
return this.vscodeTools.getTools();
75+
}
6476
return this.cachedTools;
6577
}
6678

@@ -108,15 +120,20 @@ export class McpService implements vscode.Disposable {
108120
this.output.appendLine(`[MCP] Failed to list tools for server "${name}": ${error}`);
109121
}
110122
}
111-
return allTools;
123+
return [...allTools, ...this.vscodeTools.getTools()];
112124
}
113125

114126
public async callTool(serverName: string, toolName: string, args: any): Promise<any> {
127+
if (serverName === 'vscode') {
128+
return this.vscodeTools.callTool(toolName, args);
129+
}
130+
115131
const client = this.clients.get(serverName);
116132
if (!client) {
117133
throw new Error(`MCP server "${serverName}" not found or not connected`);
118134
}
119135

136+
// Fix: Use CallToolResultSchema for validation, but return the result directly
120137
const result = await client.request(
121138
{
122139
method: 'tools/call',

src/extension.ts

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,63 @@ export function activate(context: vscode.ExtensionContext) {
2020
vscode.window.registerWebviewViewProvider(CopilotPanel.viewType, provider)
2121
);
2222

23-
// Start if enabled
23+
// Status Bar & Notifications
24+
const updateStatusBar = async () => {
25+
const status = await gateway.getStatus();
26+
if (status.running) {
27+
if (status.activeRequests > 0) {
28+
statusItem.text = `$(sync~spin) Copilot API: ${status.activeRequests}`;
29+
statusItem.tooltip = `Processing ${status.activeRequests} active request(s)`;
30+
statusItem.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground');
31+
} else {
32+
statusItem.text = '$(broadcast) Copilot API: ON';
33+
statusItem.tooltip = `Listening on ${status.config.host}:${status.config.port}`;
34+
statusItem.backgroundColor = undefined;
35+
}
36+
statusItem.show();
37+
} else {
38+
statusItem.text = '$(circle-slash) Copilot API: OFF';
39+
statusItem.tooltip = 'Copilot API server is stopped. Click to manage.';
40+
statusItem.show();
41+
}
42+
};
43+
44+
let wasRunning = false;
45+
context.subscriptions.push(gateway.onDidChangeStatus(async () => {
46+
await updateStatusBar();
47+
48+
// Notifications
49+
const status = await gateway.getStatus();
50+
if (status.running && !wasRunning) {
51+
const config = vscode.workspace.getConfiguration('githubCopilotApi.server');
52+
if (config.get<boolean>('showNotifications', true)) {
53+
const selection = await vscode.window.showInformationMessage(
54+
`GitHub Copilot API Server started at http://${status.config.host}:${status.config.port}`,
55+
'Open Dashboard'
56+
);
57+
if (selection === 'Open Dashboard') {
58+
void vscode.commands.executeCommand('github-copilot-api-vscode.openDashboard');
59+
}
60+
}
61+
}
62+
wasRunning = status.running;
63+
}));
64+
65+
// Initial State
66+
void updateStatusBar();
67+
68+
// Auto-Start Logic
2469
const config = vscode.workspace.getConfiguration('githubCopilotApi.server');
25-
const enabled = config.get('enabled', false);
26-
output.appendLine(`[DEBUG] Extension activation. Config 'enabled': ${enabled}`);
70+
const enabled = config.get<boolean>('enabled', false);
71+
const autoStart = config.get<boolean>('autoStart', false);
2772

28-
if (enabled) {
29-
statusItem.text = '$(broadcast) Copilot API: Starting…';
30-
statusItem.tooltip = 'Manage the Copilot API gateway';
31-
statusItem.show();
73+
output.appendLine(`[DEBUG] Activation. Enabled: ${enabled}, AutoStart: ${autoStart}`);
3274

75+
if (enabled || autoStart) {
3376
void gateway.start().catch(error => {
3477
output.appendLine(`[${new Date().toISOString()}] ERROR Failed to start API server: ${getErrorMessage(error)}`);
35-
statusItem.text = '$(alert) Copilot API: Failed';
36-
statusItem.tooltip = `Copilot API server failed to start: ${getErrorMessage(error)}`;
3778
void vscode.window.showErrorMessage(`Failed to start Copilot API server: ${getErrorMessage(error)}`);
3879
});
39-
} else {
40-
statusItem.text = '$(circle-slash) Copilot API: Stopped';
41-
statusItem.tooltip = 'Copilot API server is disabled (default)';
42-
statusItem.hide();
4380
}
4481

4582
const openChatCommand = vscode.commands.registerCommand('github-copilot-api-vscode.openCopilotChat', async () => {

0 commit comments

Comments
 (0)