Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Check for MCP Available Attributes #3232

Merged

Conversation

ajshedivy
Copy link
Contributor

@ajshedivy ajshedivy commented Dec 6, 2024

Description

proposed fix for #3225

Solution

Since some servers do not provider resources, tools, etc., check if client methods are available with simple wrapper:

this.client.listResources()
this.client.listTools()
this.client.listPrompts()
  async checkClientAttribute(func: () => Promise<any>): Promise<any> {
    try {
      const result = await func();
      return result;
    } catch (e) {
      return null;
    }
  }

There is prob a better way to handle this, but it's a good hot fix 😁

Checklist

  • [] The relevant docs, if any, have been updated or created

Screenshots

image

Testing

  1. Install the filesystem server:
npm install -g @modelcontextprotocol/server-filesystem
  1. Configure MCP in ~/.continue/config.json:
{
  "experimental": {
    "modelContextProtocolServer": {
      "transport": {
        "type": "stdio",
        "command": "npx",
        "args": [
          "-y",
          "@modelcontextprotocol/server-filesystem",
          "/path/to/directory"
        ]
      }
    }
  }
}

TODO

  • handle duplicate Tools:
    Adding the @modelcontextprotocol/server-filesystem server produces the following warning with the default tools:
Warning: Encountered two children with the same key, `read_file`. 
Keys should be unique so that components maintain their identity across updates. 
Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.

Copy link

netlify bot commented Dec 6, 2024

Deploy Preview for continuedev ready!

Name Link
🔨 Latest commit 2861775
🔍 Latest deploy log https://app.netlify.com/sites/continuedev/deploys/67577b658f455b0008b5b6c1
😎 Deploy Preview https://deploy-preview-3232--continuedev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@ajshedivy
Copy link
Contributor Author

ajshedivy commented Dec 6, 2024

To handle duplicate tools, an easy fix would just be to encode the BuiltinTools using some sort of prefix:

export enum BuiltInToolNames {
  ReadFile = "builtin_read_file",
  ReadCurrentlyOpenFile = "builtin_read_currently_open_file",
  CreateNewFile = "builtin_create_new_file",
  RunTerminalCommand = "builtin_run_terminal_command",
  ViewSubdirectory = "builtin_view_subdirectory",
  ViewRepoMap = "builtin_view_repo_map",
  ExactSearch = "builtin_exact_search",
  SearchWeb = "builtin_search_web",
  ViewDiff = "builtin_view_diff",
}

@ajshedivy ajshedivy marked this pull request as ready for review December 9, 2024 14:16
@ajshedivy
Copy link
Contributor Author

@sestinj @Patrick-Erichsen This is ready for review!

@SimonB97
Copy link

SimonB97 commented Dec 9, 2024

I tried to get this to work on my end, could you confirm my config.json structure please? I put "experimental" conf at the root (and at the end).

{
  "models": [
    {
      "title": "Claude 3.5 Sonnet (Free Trial)",
      "provider": "free-trial",
      "model": "claude-3-5-sonnet-latest",
      "systemMessage": "You are an expert software developer. You give helpful and concise responses."
    },
    {
      "title": "GPT-4o (Free Trial)",
      "provider": "free-trial",
      "model": "gpt-4o",
      "systemMessage": "You are an expert software developer. You give helpful and concise responses."
    },
    {
      "title": "Llama3.1 70b (Free Trial)",
      "provider": "free-trial",
      "model": "llama3.1-70b",
      "systemMessage": "You are an expert software developer. You give helpful and concise responses."
    },
    {
      "title": "Codestral (Free Trial)",
      "provider": "free-trial",
      "model": "codestral-latest",
      "systemMessage": "You are an expert software developer. You give helpful and concise responses."
    },
    {
      "model": "claude-3-5-sonnet-latest",
      "provider": "anthropic",
      "apiKey": "",
      "title": "Claude 3.5 Sonnet"
    }
  ],
  "tabAutocompleteModel": {
    "title": "Tab Autocomplete",
    "provider": "free-trial",
    "model": "codestral-latest"
  },
  "customCommands": [
    {
      "name": "test",
      "prompt": "{{{ input }}}\n\nWrite a comprehensive set of unit tests for the selected code. It should setup, run tests that check for correctness including important edge cases, and teardown. Ensure that the tests are complete and sophisticated. Give the tests just as chat output, don't edit any file.",
      "description": "Write unit tests for highlighted code"
    }
  ],
  "contextProviders": [
    {
      "name": "code",
      "params": {}
    },
    {
      "name": "docs",
      "params": {}
    },
    {
      "name": "diff",
      "params": {}
    },
    {
      "name": "terminal",
      "params": {}
    },
    {
      "name": "problems",
      "params": {}
    },
    {
      "name": "folder",
      "params": {}
    },
    {
      "name": "codebase",
      "params": {}
    }
  ],
  "slashCommands": [
    {
      "name": "share",
      "description": "Export the current chat session to markdown"
    },
    {
      "name": "cmd",
      "description": "Generate a shell command"
    },
    {
      "name": "commit",
      "description": "Generate a git commit message"
    }
  ],
  "experimental": {
    "modelContextProtocolServer": {
      "transport": {
        "type": "stdio",
        "command": "npx",
        "args": [
          "-y",
          "@modelcontextprotocol/server-filesystem",
          "~/Desktop"
        ]
      }
    }
  },
  "embeddingsProvider": {
    "provider": "free-trial"
  },
  "reranker": {
    "name": "free-trial"
  }
}

@Patrick-Erichsen Patrick-Erichsen self-assigned this Dec 9, 2024
@Patrick-Erichsen
Copy link
Collaborator

@adamszewe thanks for opening this!

I poked around the TS interface for client a bit and I think we can use client.getServerCapabilities in the modifyConfig to do this a bit cleaner.

https://github.com/search?q=repo%3Amodelcontextprotocol%2Ftypescript-sdk%20getServerCapabilities&type=code

--
150 | * After initialization has completed, this will be populated with the server's reported capabilities.
151 | */
152 | getServerCapabilities(): ServerCapabilities \| undefined {
153 | return this._serverCapabilities;
154 | }


// Server supports resources and tools, but not prompts
--
214 | expect(client.getServerCapabilities()).toEqual({
215 | resources: {},
216 | tools: {},
217 | });

I think our logic could then look something like this:

    const capabilities = this.client.getServerCapabilities()

    if (capabilities?.resources) {
      const resources = this.client.listResources()
      // ...
    }

+1 to the implementation for BuiltInToolNames, I think the prefix is a good call.

Copy link
Contributor

@sestinj sestinj left a comment

Choose a reason for hiding this comment

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

@ajshedivy I think the right way to do this is probably with the client.getServerCapabilities() method on the MCP client. Any interest in attempting that?

Edit: totally missed that Patrick caught this above, ignore me!

@sestinj sestinj requested review from sestinj and removed request for sestinj December 9, 2024 21:51
@ajshedivy
Copy link
Contributor Author

@adamszewe thanks for opening this!

I poked around the TS interface for client a bit and I think we can use client.getServerCapabilities in the modifyConfig to do this a bit cleaner.

https://github.com/search?q=repo%3Amodelcontextprotocol%2Ftypescript-sdk%20getServerCapabilities&type=code

--
150 | * After initialization has completed, this will be populated with the server's reported capabilities.
151 | */
152 | getServerCapabilities(): ServerCapabilities \| undefined {
153 | return this._serverCapabilities;
154 | }


// Server supports resources and tools, but not prompts
--
214 | expect(client.getServerCapabilities()).toEqual({
215 | resources: {},
216 | tools: {},
217 | });

I think our logic could then look something like this:

    const capabilities = this.client.getServerCapabilities()

    if (capabilities?.resources) {
      const resources = this.client.listResources()
      // ...
    }

+1 to the implementation for BuiltInToolNames, I think the prefix is a good call.

This would be much cleaner 😁

@adamszewe thanks for opening this!

I poked around the TS interface for client a bit and I think we can use client.getServerCapabilities in the modifyConfig to do this a bit cleaner.

https://github.com/search?q=repo%3Amodelcontextprotocol%2Ftypescript-sdk%20getServerCapabilities&type=code

--
150 | * After initialization has completed, this will be populated with the server's reported capabilities.
151 | */
152 | getServerCapabilities(): ServerCapabilities \| undefined {
153 | return this._serverCapabilities;
154 | }


// Server supports resources and tools, but not prompts
--
214 | expect(client.getServerCapabilities()).toEqual({
215 | resources: {},
216 | tools: {},
217 | });

I think our logic could then look something like this:

    const capabilities = this.client.getServerCapabilities()

    if (capabilities?.resources) {
      const resources = this.client.listResources()
      // ...
    }

+1 to the implementation for BuiltInToolNames, I think the prefix is a good call.

@Patrick-Erichsen Good find! much better solution 😁

@sestinj sestinj merged commit ebad743 into continuedev:main Dec 11, 2024
5 checks passed
@YanxingLiu
Copy link

I tried to get this to work on my end, could you confirm my config.json structure please? I put "experimental" conf at the root (and at the end).

{
  "models": [
    {
      "title": "Claude 3.5 Sonnet (Free Trial)",
      "provider": "free-trial",
      "model": "claude-3-5-sonnet-latest",
      "systemMessage": "You are an expert software developer. You give helpful and concise responses."
    },
    {
      "title": "GPT-4o (Free Trial)",
      "provider": "free-trial",
      "model": "gpt-4o",
      "systemMessage": "You are an expert software developer. You give helpful and concise responses."
    },
    {
      "title": "Llama3.1 70b (Free Trial)",
      "provider": "free-trial",
      "model": "llama3.1-70b",
      "systemMessage": "You are an expert software developer. You give helpful and concise responses."
    },
    {
      "title": "Codestral (Free Trial)",
      "provider": "free-trial",
      "model": "codestral-latest",
      "systemMessage": "You are an expert software developer. You give helpful and concise responses."
    },
    {
      "model": "claude-3-5-sonnet-latest",
      "provider": "anthropic",
      "apiKey": "",
      "title": "Claude 3.5 Sonnet"
    }
  ],
  "tabAutocompleteModel": {
    "title": "Tab Autocomplete",
    "provider": "free-trial",
    "model": "codestral-latest"
  },
  "customCommands": [
    {
      "name": "test",
      "prompt": "{{{ input }}}\n\nWrite a comprehensive set of unit tests for the selected code. It should setup, run tests that check for correctness including important edge cases, and teardown. Ensure that the tests are complete and sophisticated. Give the tests just as chat output, don't edit any file.",
      "description": "Write unit tests for highlighted code"
    }
  ],
  "contextProviders": [
    {
      "name": "code",
      "params": {}
    },
    {
      "name": "docs",
      "params": {}
    },
    {
      "name": "diff",
      "params": {}
    },
    {
      "name": "terminal",
      "params": {}
    },
    {
      "name": "problems",
      "params": {}
    },
    {
      "name": "folder",
      "params": {}
    },
    {
      "name": "codebase",
      "params": {}
    }
  ],
  "slashCommands": [
    {
      "name": "share",
      "description": "Export the current chat session to markdown"
    },
    {
      "name": "cmd",
      "description": "Generate a shell command"
    },
    {
      "name": "commit",
      "description": "Generate a git commit message"
    }
  ],
  "experimental": {
    "modelContextProtocolServer": {
      "transport": {
        "type": "stdio",
        "command": "npx",
        "args": [
          "-y",
          "@modelcontextprotocol/server-filesystem",
          "~/Desktop"
        ]
      }
    }
  },
  "embeddingsProvider": {
    "provider": "free-trial"
  },
  "reranker": {
    "name": "free-trial"
  }
}

Hello, have you solved the problem. I encountered the same issue that I have added the modelContextProtocolServers, but I cannnot see the mcp after I typed @.

@SimonB97
Copy link

Tried last time a few days ago (maybe they have updated it since) but sadly still didn't work for me

@ajshedivy
Copy link
Contributor Author

@SimonB97 @YanxingLiu the config has actually changed in the recent pre-release:
change modelContextProtocolServer -> modelContextProtocolServers (severs in now plural). I opened a PR with some potential fixes for multiple servers: #3462

@YanxingLiu
Copy link

Contributor

Thanks for your work, I have tried the setting mentioned in #3462. But it still failed. The detail is shown in that PR. I would like to ask if you know why this is. Thanks a lot for your help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants