Working with GT as an MCP server
GT ships with a minimal implementation of an MCP server that can execute tools, expose resources, and define prompts.
N.B.: This server was neither hardened nor audited. It is a simple Zinc server that might crash or, depending on the capabilities you give the tools it exposes, do undesirable things to your image or system at large. Use with caution at this time.
To start an MCP server on port 3000, execute the following snippet.
server := GtMcpServer new port: 3000; start
To stop the server, you can call GtMcpServer>>#stop
on it.
server stop
Once we have a server, we can start by adding tools to it. The tools use the same model as in Adding custom tools to assistants and can thus be shared across assistants and servers.
server addTool: (GtLlmFunctionTool new name: 'getImplementors'; parameters: {'methodName'}; description: 'Gets a method by name and returns a list of methods that implement it.'; block: [ :functionCall | Character cr join: (functionCall anyArgument asSymbol gtImplementors result toArray wait collect: #name) ])
Once we have this server, we can test it by connecting to it using a client.
client := GtMcpClient new transport: (GtMcpHttpTransport new url: 'http://localhost:3000/')
We can confirm the presence of the tools, either through navigating to the view on the client, or programmatically.
client listTools
Once we’ve confirmed the presence of the tool, we can call it.
client callTool: 'getImplementors' withArguments: {'methodName' -> 'callTool:withArguments:'} asDictionary
Like tools, we can expose resources on the server. We define them using a model and add them to the server. For the purposes of this example, we take a random page from the Glamorous Toolkit Book.
page := LeDatabase gtBook pages atRandom. pageUri := 'lepage://' , page database properties databaseName , ':' , page title. server addResource: (GtMcpResource new name: page title; uri: pageUri; mimeType: ZnMimeType textPlain; contentsBlock: [ GtLlmPageExporter new page: page; export ])
Once again, we can define a client (if you haven’t already).
client := GtMcpClient new transport: (GtMcpHttpTransport new url: 'http://localhost:3000/')
We can then query for resources
client listResources
or get them directly.
(client readResource: pageUri) first at: 'text'
Lastly, the server can also generate prompts. Once again, we need to define one and add it to the server.
server addPrompt: (GtMcpPrompt new name: 'Code Review'; description: 'Asks the LLM to analyze code quality and suggest improvements'; arguments: {'code'}; promptBlock: [ :arguments | {GtLlmUserMessage new content: {'type' -> 'text'. 'text' -> ('Review the following code as if you were a senior developer: ``` ' , (arguments at: 'code') , ' ```')} asDictionary} ])
Once we have the prompt, we can then, again, optionally create a client.
client := GtMcpClient new transport: (GtMcpHttpTransport new url: 'http://localhost:3000/')
And query for prompts
client listPrompts
Or create messages from it directly.
(client messagesFromPrompt: 'Code Review' withArguments: {'code' -> '(defn a-clojure-fn [x] (* x 2))'} asDictionary) first