Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7,613 changes: 1,371 additions & 6,242 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
}
},
"dependencies": {
"@augmentcode/auggie-sdk": "^0.1.14",
"@augmentcode/auggie-sdk": "^0.1.15",
"cheerio": "^1.1.2",
"commander": "^12.0.0",
"ignore": "^5.3.0",
Expand Down
6 changes: 6 additions & 0 deletions src/bin/cmd-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as readline from "readline";
import { CLIAgent, type Provider } from "../clients/cli-agent.js";
import { MultiIndexRunner } from "../clients/multi-index-runner.js";
import { CompositeStoreReader, parseIndexSpecs } from "../stores/index.js";
import { buildClientUserAgent } from "../core/utils.js";

const PROVIDER_DEFAULTS: Record<Provider, string> = {
openai: "gpt-5-mini",
Expand Down Expand Up @@ -50,9 +51,13 @@ export const agentCommand = new Command("agent")
const store = await CompositeStoreReader.fromSpecs(specs);

// Create multi-index runner
// Build User-Agent for analytics tracking
const clientUserAgent = buildClientUserAgent("cli");

const runner = await MultiIndexRunner.create({
store,
searchOnly: options.searchOnly,
clientUserAgent,
});

console.log("\x1b[1;36mContext Connectors Minimal Agent\x1b[0m");
Expand All @@ -73,6 +78,7 @@ export const agentCommand = new Command("agent")
model,
maxSteps: options.maxSteps,
verbose: options.verbose,
clientUserAgent,
});
await agent.initialize();

Expand Down
5 changes: 4 additions & 1 deletion src/bin/cmd-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Indexer } from "../core/indexer.js";
import { Source } from "../sources/types.js";
import { FilesystemStore } from "../stores/filesystem.js";
import { getS3Config } from "../stores/s3-config.js";
import { buildClientUserAgent } from "../core/utils.js";

// Shared store options
interface StoreOptions {
Expand Down Expand Up @@ -49,7 +50,9 @@ async function runIndex(
sourceType: string
) {
console.log(`Indexing ${sourceType} source...`);
const indexer = new Indexer();
const indexer = new Indexer({
clientUserAgent: buildClientUserAgent("cli"),
});
const result = await indexer.index(source, store, indexKey);

console.log(`\nIndexing complete!`);
Expand Down
2 changes: 2 additions & 0 deletions src/bin/cmd-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getSourceIdentifier } from "../core/types.js";
import { getS3Config } from "../stores/s3-config.js";
import { parseIndexSpec } from "../stores/index-spec.js";
import type { IndexStoreReader } from "../stores/types.js";
import { buildClientUserAgent } from "../core/utils.js";

export const searchCommand = new Command("search")
.description("Search indexed content and answer questions (use --raw for raw results)")
Expand Down Expand Up @@ -83,6 +84,7 @@ export const searchCommand = new Command("search")
const client = new SearchClient({
store,
indexName: indexKey,
clientUserAgent: buildClientUserAgent("cli"),
});

await client.initialize();
Expand Down
20 changes: 18 additions & 2 deletions src/clients/cli-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ export interface CLIAgentSingleConfig {
stream?: boolean;
/** Custom system prompt. Uses a sensible default if not provided. */
systemPrompt?: string;
/**
* Custom User-Agent string for analytics tracking.
* When provided, this is sent to the Augment API for usage analytics.
* Only used when provider is 'augment'.
*/
clientUserAgent?: string;
}

/**
Expand Down Expand Up @@ -110,6 +116,12 @@ export interface CLIAgentMultiConfig {
stream?: boolean;
/** Custom system prompt. Uses a sensible default if not provided. */
systemPrompt?: string;
/**
* Custom User-Agent string for analytics tracking.
* When provided, this is sent to the Augment API for usage analytics.
* Only used when provider is 'augment'.
*/
clientUserAgent?: string;
}

/** Configuration for the CLI agent */
Expand All @@ -136,7 +148,8 @@ Be concise but thorough. Reference specific files and line numbers when helpful.
*/
async function loadModel(
provider: Provider,
modelName: string
modelName: string,
clientUserAgent?: string
): Promise<LanguageModel> {
switch (provider) {
case "openai": {
Expand Down Expand Up @@ -179,6 +192,7 @@ async function loadModel(
return new AugmentLanguageModel(modelName, {
apiKey: credentials.apiKey,
apiUrl: credentials.apiUrl,
clientUserAgent,
}) as unknown as LanguageModel;
}
default:
Expand Down Expand Up @@ -223,6 +237,7 @@ export class CLIAgent {
private readonly verbose: boolean;
private readonly stream: boolean;
private readonly systemPrompt: string;
private readonly clientUserAgent?: string;
private readonly tools: ToolSet;
private messages: CoreMessage[] = [];

Expand All @@ -242,6 +257,7 @@ export class CLIAgent {
this.verbose = config.verbose ?? false;
this.stream = config.stream ?? true;
this.systemPrompt = config.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
this.clientUserAgent = config.clientUserAgent;
this.tools = this.runner ? this.createMultiIndexTools() : this.createSingleClientTools();
}

Expand Down Expand Up @@ -393,7 +409,7 @@ export class CLIAgent {
* @throws Error if the provider package is not installed
*/
async initialize(): Promise<void> {
this.model = await loadModel(this.provider, this.modelName);
this.model = await loadModel(this.provider, this.modelName, this.clientUserAgent);
}

/**
Expand Down
37 changes: 18 additions & 19 deletions src/clients/mcp-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ import {
} from "@modelcontextprotocol/sdk/types.js";
import type { IndexStoreReader } from "../stores/types.js";
import { MultiIndexRunner } from "./multi-index-runner.js";
import { buildClientUserAgent, type MCPClientInfo } from "../core/utils.js";
import {
SEARCH_DESCRIPTION,
LIST_FILES_DESCRIPTION,
READ_FILE_DESCRIPTION,
withIndexList,
} from "./tool-descriptions.js";

/**
* Configuration for the MCP server.
*/
Expand All @@ -72,7 +72,6 @@ export interface MCPServerConfig {
*/
version?: string;
}

/**
* Create an MCP server instance.
*
Expand All @@ -96,18 +95,19 @@ export async function createMCPServer(
config: MCPServerConfig
): Promise<Server> {
// Create shared runner for multi-index operations
// Build User-Agent for analytics tracking
const clientUserAgent = buildClientUserAgent("mcp");

const runner = await MultiIndexRunner.create({
store: config.store,
indexNames: config.indexNames,
searchOnly: config.searchOnly,
clientUserAgent,
});

const { indexNames, indexes } = runner;
const searchOnly = !runner.hasFileOperations();

// Format index list for tool descriptions
const indexListStr = runner.getIndexListString();

// Create MCP server
const server = new Server(
{
Expand All @@ -120,7 +120,19 @@ export async function createMCPServer(
},
}
);

// Use the SDK's oninitialized callback to capture MCP client info
// This preserves the SDK's protocol version negotiation
server.oninitialized = () => {
const clientInfo = server.getClientVersion();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we confirm @modelcontextprotocol/sdk (at our pinned version) actually exposes server.getClientVersion() and the server.oninitialized callback? If either isn’t supported, this will throw at runtime and prevent the MCP server from starting.

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed! Both APIs exist in @modelcontextprotocol/sdk version 1.25.3:

if (clientInfo) {
const mcpClientInfo: MCPClientInfo = {
name: clientInfo.name,
version: clientInfo.version,
};
const updatedUserAgent = buildClientUserAgent("mcp", mcpClientInfo);
runner.updateClientUserAgent(updatedUserAgent);
}
};
// Define tool type for type safety
type Tool = {
name: string;
Expand All @@ -134,12 +146,10 @@ export async function createMCPServer(
required?: string[];
};
};

// Tool descriptions with available indexes (from shared module)
const searchDescription = withIndexList(SEARCH_DESCRIPTION, indexListStr);
const listFilesDescription = withIndexList(LIST_FILES_DESCRIPTION, indexListStr);
const readFileDescription = withIndexList(READ_FILE_DESCRIPTION, indexListStr);

// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
const tools: Tool[] = [
Expand Down Expand Up @@ -167,7 +177,6 @@ export async function createMCPServer(
},
},
];

// Only advertise file tools if not in search-only mode
if (!searchOnly) {
tools.push(
Expand Down Expand Up @@ -247,18 +256,14 @@ export async function createMCPServer(
}
);
}

return { tools };
});

// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;

try {
const indexName = args?.index_name as string;
const client = await runner.getClient(indexName);

switch (name) {
case "search": {
const result = await client.search(args?.query as string, {
Expand All @@ -270,7 +275,6 @@ export async function createMCPServer(
],
};
}

case "list_files": {
if (searchOnly) {
return {
Expand All @@ -291,7 +295,6 @@ export async function createMCPServer(
content: [{ type: "text", text }],
};
}

case "read_file": {
if (searchOnly) {
return {
Expand Down Expand Up @@ -321,7 +324,6 @@ export async function createMCPServer(
content: [{ type: "text", text: result.contents ?? "" }],
};
}

default:
return {
content: [{ type: "text", text: `Unknown tool: ${name}` }],
Expand All @@ -335,10 +337,8 @@ export async function createMCPServer(
};
}
});

return server;
}

/**
* Run an MCP server with stdio transport.
*
Expand Down Expand Up @@ -369,4 +369,3 @@ export async function runMCPServer(config: MCPServerConfig): Promise<void> {
const transport = new StdioServerTransport();
await server.connect(transport);
}

24 changes: 22 additions & 2 deletions src/clients/multi-index-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export interface MultiIndexRunnerConfig {
* When true, only search is available.
*/
searchOnly?: boolean;
/**
* Custom User-Agent string for analytics tracking.
* When provided, this is passed to SearchClient instances for API requests.
*/
clientUserAgent?: string;
}

/** Create a Source from index state metadata */
Expand Down Expand Up @@ -65,6 +70,7 @@ async function createSourceFromState(state: IndexStateSearchOnly): Promise<Sourc
export class MultiIndexRunner {
private readonly store: IndexStoreReader;
private readonly searchOnly: boolean;
private clientUserAgent?: string;
private readonly clientCache = new Map<string, SearchClient>();

/** Available index names */
Expand All @@ -77,12 +83,14 @@ export class MultiIndexRunner {
store: IndexStoreReader,
indexNames: string[],
indexes: IndexInfo[],
searchOnly: boolean
searchOnly: boolean,
clientUserAgent?: string
) {
this.store = store;
this.indexNames = indexNames;
this.indexes = indexes;
this.searchOnly = searchOnly;
this.clientUserAgent = clientUserAgent;
}

/**
Expand Down Expand Up @@ -132,7 +140,18 @@ export class MultiIndexRunner {
throw new Error("No valid indexes available (all indexes failed to load)");
}

return new MultiIndexRunner(store, validIndexNames, indexes, searchOnly);
return new MultiIndexRunner(store, validIndexNames, indexes, searchOnly, config.clientUserAgent);
}


/**
* Update the User-Agent string.
*
* Call this after receiving MCP client info to include the client name/version.
* Note: Only affects future client creations, not existing cached clients.
*/
updateClientUserAgent(newUserAgent: string): void {
this.clientUserAgent = newUserAgent;
}

/**
Expand All @@ -158,6 +177,7 @@ export class MultiIndexRunner {
store: this.store,
source,
indexName,
clientUserAgent: this.clientUserAgent,
});
await client.initialize();
this.clientCache.set(indexName, client);
Expand Down
8 changes: 8 additions & 0 deletions src/clients/search-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ export interface SearchClientConfig {
* @default process.env.AUGMENT_API_URL
*/
apiUrl?: string;
/**
* Custom User-Agent string for analytics tracking.
* When provided, this is sent to the Augment API for usage analytics.
*/
clientUserAgent?: string;
}

/**
Expand Down Expand Up @@ -106,6 +111,7 @@ export class SearchClient {
private indexName: string;
private apiKey: string;
private apiUrl: string;
private clientUserAgent?: string;

private context: DirectContext | null = null;
private state: IndexStateSearchOnly | null = null;
Expand All @@ -123,6 +129,7 @@ export class SearchClient {
this.indexName = config.indexName;
this.apiKey = config.apiKey ?? process.env.AUGMENT_API_TOKEN ?? "";
this.apiUrl = config.apiUrl ?? process.env.AUGMENT_API_URL ?? "";
this.clientUserAgent = config.clientUserAgent;
}

/**
Expand Down Expand Up @@ -162,6 +169,7 @@ export class SearchClient {
this.context = await DirectContext.import(this.state.contextState, {
apiKey: this.apiKey,
apiUrl: this.apiUrl,
clientUserAgent: this.clientUserAgent,
});
}

Expand Down
3 changes: 2 additions & 1 deletion src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export {
shouldFilterFile,
} from "./file-filter.js";

export { sanitizeKey, isoTimestamp } from "./utils.js";
export { sanitizeKey, isoTimestamp, buildClientUserAgent } from "./utils.js";
export type { ClientProduct, MCPClientInfo } from "./utils.js";

export { Indexer } from "./indexer.js";
export type { IndexerConfig } from "./indexer.js";
Expand Down
Loading
Loading