Skip to content

Commit

Permalink
Adding MCP tools
Browse files Browse the repository at this point in the history
  • Loading branch information
arafatkatze committed Jan 11, 2025
1 parent f6499f4 commit 05f7fc9
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 10 deletions.
1 change: 1 addition & 0 deletions lib/shared/src/context/openctx/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const REMOTE_DIRECTORY_PROVIDER_URI = 'internal-remote-directory-search'
export const WEB_PROVIDER_URI = 'internal-web-provider'
export const GIT_OPENCTX_PROVIDER_URI = 'internal-git-openctx-provider'
export const CODE_SEARCH_PROVIDER_URI = 'internal-code-search-provider'
export const MODEL_CONTEXT_PROVIDER_URI = 'internal-model-context-provider'
export const WORKSPACE_DIRECTORY_PROVIDER_URI = DOTCOM_WORKSPACE_UPGRADE_URL.href + '?workspace=dir'
export const WORKSPACE_REPOSITORY_PROVIDER_URI = DOTCOM_WORKSPACE_UPGRADE_URL.href + '?workspace=repo'

Expand Down
1 change: 1 addition & 0 deletions lib/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ export {
WEB_PROVIDER_URI,
GIT_OPENCTX_PROVIDER_URI,
CODE_SEARCH_PROVIDER_URI,
MODEL_CONTEXT_PROVIDER_URI,
WORKSPACE_REPOSITORY_PROVIDER_URI,
WORKSPACE_DIRECTORY_PROVIDER_URI,
isRemoteWorkspaceProvider,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"update-agent-recordings": "pnpm build && CODY_KEEP_UNUSED_RECORDINGS=false CODY_RECORD_IF_MISSING=true vitest agent/src",
"update-agent-recordings-windows": "PowerShell -ExecutionPolicy Bypass -Command \"pnpm build; if ($?) { $Env:CODY_KEEP_UNUSED_RECORDINGS='false'; $Env:CODY_RECORD_IF_MISSING='true'; vitest agent/src }\"",
"update-rewrite-recordings": "rm -rf recordings && CODY_RECORD_IF_MISSING=true CODY_RECORDING_MODE=record vitest vscode/src/local-context/rewrite-keyword-query.test.ts",
"openctx:link": "cd ../openctx && pnpm -C lib/client link --global && pnpm -C lib/schema link --global && pnpm -C lib/protocol link --global && pnpm -C client/vscode-lib link --global && cd ../cody && pnpm link --global @openctx/client && pnpm link --global @openctx/schema && pnpm link --global @openctx/protocol && cd vscode && pnpm link --global @openctx/vscode-lib",
"openctx:link": "cd ../openctx && pnpm -C lib/client link --global && pnpm -C lib/schema link --global && pnpm -C lib/protocol link --global && pnpm -C client/vscode-lib link --global && pnpm -C provider/modelcontextprotocoltools link --global && cd ../cody && pnpm link --global @openctx/client && pnpm link --global @openctx/schema && pnpm link --global @openctx/protocol && pnpm link --global @openctx/provider-modelcontextprotocoltools && cd vscode && pnpm link --global @openctx/vscode-lib",
"openctx:unlink": "pnpm unlink --global @openctx/client && pnpm unlink --global @openctx/schema && pnpm unlink --global @openctx/protocol && cd vscode && pnpm unlink --global @openctx/vscode-lib",
"vsce-version-bump": "pnpm -C vscode version-bump:minor"
},
Expand Down
51 changes: 50 additions & 1 deletion vscode/src/chat/agentic/CodyTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
type ContextItemOpenCtx,
ContextItemSource,
type ContextMentionProviderMetadata,
MODEL_CONTEXT_PROVIDER_URI,
PromptString,
firstValueFrom,
logDebug,
Expand All @@ -15,6 +16,7 @@ import {
import { URI } from 'vscode-uri'
import { getContextFromRelativePath } from '../../commands/context/file-path'
import { getContextFileFromShell } from '../../commands/context/shell'
import type { OpenCtxProvider } from '../../context/openctx/types'
import { type ContextRetriever, toStructuredMentions } from '../chat-view/ContextRetriever'
import { getChatContextItemsForMention } from '../context/chatContext'
import { getCorpusContextItemsForEditorState } from '../initialContext'
Expand Down Expand Up @@ -78,7 +80,7 @@ export abstract class CodyTool {
/**
* Resets the raw text input stream.
*/
private reset(): void {
public reset(): void {
this.unprocessedText = ''
}
/**
Expand Down Expand Up @@ -235,6 +237,53 @@ class SearchTool extends CodyTool {
}
}

export class ModelContextProviderTool extends CodyTool {
constructor(
config: CodyToolConfig,
private modelContextProvider: OpenCtxProvider,
private toolName: string
) {
super(config)
}

public parse(): string[] {
const parsed = JSON.parse(this.unprocessedText)
console.log(' the parsed for me is ', parsed)
const beforeCleaup = this.unprocessedText
this.reset()
return [beforeCleaup]
}

public async execute(span: Span, queries: string[]): Promise<ContextItem[]> {
span.addEvent('executeModelContextProviderTool')

try {
const rawItems =
(await this.modelContextProvider.items?.(
{ mention: { title: this.toolName, data: JSON.parse('{}'), uri: '' } },
{}
)) ?? []

return rawItems.map(item => ({
type: 'openctx',
provider: 'openctx',
title: item.title,
uri: URI.parse(''),
providerUri: MODEL_CONTEXT_PROVIDER_URI,
content: item.ai?.content || '',
mention: {
uri: '',
data: item.ai,
description: item.ai?.content,
},
}))
} catch (error) {
console.error('ModelContextProviderTool execution failed:', error)
return []
}
}
}

/**
* Tool for interacting with OpenCtx providers and retrieving context items.
*/
Expand Down
90 changes: 87 additions & 3 deletions vscode/src/chat/agentic/CodyToolProvider.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { authStatus, firstValueFrom, isDefined, ps } from '@sourcegraph/cody-shared'
import { PromptString, authStatus, firstValueFrom, isDefined, ps } from '@sourcegraph/cody-shared'
import { getOpenCtxProviders } from '../../context/openctx'
import { createModelContextProvider } from '../../context/openctx/modelContextProvider'
import type { OpenCtxProvider } from '../../context/openctx/types'
import type { ContextRetriever } from '../chat-view/ContextRetriever'
import {
type CodyTool,
type CodyToolConfig,
ModelContextProviderTool,
OpenCtxTool,
getDefaultCodyTools,
registerDefaultTools,
} from './CodyTool'

interface CodyShellConfig {
user?: boolean
instance?: boolean
Expand All @@ -33,6 +35,7 @@ export interface ToolStatusCallback {
*/
export class CodyToolProvider {
private openCtxTools: CodyTool[] = []
private modelContextProviderTools: CodyTool[] = []
private toolFactory = new ToolFactory()
private shellConfig: CodyShellConfig = {
user: false,
Expand All @@ -43,6 +46,7 @@ export class CodyToolProvider {
private constructor(private contextRetriever: Pick<ContextRetriever, 'retrieveContext'>) {
this.initializeToolRegistry()
this.initializeOpenCtxTools()
this.initializeModelContextProviderTools()
}

public static instance(
Expand Down Expand Up @@ -71,13 +75,93 @@ export class CodyToolProvider {
this.contextRetriever,
this.toolFactory
)
return [...defaultTools, ...this.openCtxTools]
return [...defaultTools, ...this.openCtxTools, ...this.modelContextProviderTools]
}

private async initializeOpenCtxTools(): Promise<void> {
this.openCtxTools = await this.buildOpenCtxCodyTools()
}

private async initializeModelContextProviderTools(): Promise<void> {
const modelContextProvider = await createModelContextProvider()
await modelContextProvider.meta({}, {})
this.modelContextProviderTools = await this.buildModelContextProviderTools(modelContextProvider)
}

private async buildModelContextProviderTools(
modelContextProvider: OpenCtxProvider
): Promise<CodyTool[]> {
const mentions = await modelContextProvider.mentions?.({ query: '' }, {})
if (!mentions?.length) {
return []
}

// Create tools from mentions
return mentions
.map(mention => {
const toolName = `MCP-${mention.title}`
const upperTitle = mention.title.toUpperCase()
const tagName = `${upperTitle}TOOL`

// Create config in OPENCTX_CONFIG format
const config = {
title: `${mention.title} (via MCP)`,
tags: {
tag: PromptString.unsafe_fromUserQuery(tagName),
subTag: ps``,
},
prompt: {
instruction: PromptString.unsafe_fromUserQuery(
`Use ${mention.title} to ${mention.description || 'retrieve context'}. ` +
`Input must follow this schema: ${JSON.stringify(
mention.data?.properties,
null,
2
)}` +
// Add validation rules
'Ensure all required properties are provided and types match the schema.'
),
placeholder: PromptString.unsafe_fromUserQuery(
`${mention.title.toUpperCase()}_INPUT`
),
example: PromptString.unsafe_fromUserQuery(
`To use ${mention.title} with valid schema: \`<${tagName}>${JSON.stringify({
message: mention.data?.properties || 'example input',
})}</${tagName}>\``
),
},
}
const instructstring = this.getInstruction(config)
console.log(' the instructstring for me is ', instructstring)

// Register the tool
this.toolFactory.registry.register({
name: toolName,
...config,
createInstance: toolConfig =>
new ModelContextProviderTool(
toolConfig as CodyToolConfig,
modelContextProvider,
mention.title
),
})

const tool = this.toolFactory.createTool(toolName)
if (tool) {
this.modelContextProviderTools.push(tool)
}
return tool
})
.filter(isDefined)
}

private getInstruction(config: CodyToolConfig): PromptString {
const { tag } = config.tags
const { instruction, placeholder } = config.prompt
const instructionString = ps`${instruction}: \`<${tag}>${placeholder}</${tag}>\``
return ps`${instructionString}`
}

private async buildOpenCtxCodyTools(): Promise<CodyTool[]> {
const OPENCTX_CONFIG = {
'internal-web-provider': {
Expand Down
8 changes: 3 additions & 5 deletions vscode/src/context/openctx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import type {
ImportedProviderConfiguration,
ClientConfiguration as OpenCtxClientConfiguration,
} from '@openctx/client'

import type { createController } from '@openctx/vscode-lib'
import { Observable, map } from 'observable-fns'
import { logDebug } from '../output-channel-logger'
Expand All @@ -42,13 +43,11 @@ import {
RemoteWorkspaceRepositoryProvider,
} from './openctx/remoteSourcegraphTeams'
import { createWebProvider } from './openctx/web'

export function exposeOpenCtxClient(
context: Pick<vscode.ExtensionContext, 'extension' | 'secrets'>,
createOpenCtxController: typeof createController | undefined
): Observable<void> {
void warnIfOpenCtxExtensionConflict()

): Observable<any> {
warnIfOpenCtxExtensionConflict()
return combineLatest(
resolvedConfig.pipe(
map(({ configuration: { experimentalNoodle } }) => ({
Expand Down Expand Up @@ -201,7 +200,6 @@ export function getOpenCtxProviders(
providerUri: CODE_SEARCH_PROVIDER_URI,
})
}

return providers
}
)
Expand Down
39 changes: 39 additions & 0 deletions vscode/src/context/openctx/modelContextProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import proxy from '@openctx/provider-modelcontextprotocoltools'

import { MODEL_CONTEXT_PROVIDER_URI } from '@sourcegraph/cody-shared'
import type { OpenCtxProvider } from './types'

export async function createModelContextProvider(): Promise<OpenCtxProvider> {
return {
providerUri: MODEL_CONTEXT_PROVIDER_URI,

async meta() {
const client = await proxy.meta!(
{},
{
'mcp.provider.uri':
'file:///Users/arafatkhan/Desktop/servers/src/everything/dist/index.js',
'mcp.provider.args': [],
}
)
console.log(' the client for me is ', client)

return {
name: 'Code Search',
}
},
async mentions({ query }) {
// returns a list of available tools in MCP
const items = await proxy.mentions!({ query: 'noti' }, {})
console.log(' the items for me are ', items)
return items
},

async items({ mention }) {
// returns the result of a speciic tool of MCP
const items = await proxy.items!({ mention }, {})
console.log(' the items for me are ', items)
return items
},
}
}

0 comments on commit 05f7fc9

Please sign in to comment.