Monday, September 22, 2025

NPX


 

In this post we will review NPX the Node Package Execute tool.

NPX is a command line util that is installed as part of the Node installation. Notice that this means that npx version is coupled with the node version.


The NPX temporary installs of packages that are used for a "script like"execution of a package. Instead of using npm to globally install a package and then run it, npx handles both.


The first step of NPX is to download the required package and its dependencies. The download target is:

~/.npm/_npx/<HASH>

The hash is based on the name and version of the package that NPX runs. Notice that the folder is never automatically removed, so once downloaded it will not be re-downloaded, that is unless we manually remove the cache folder or run a different version.

NPX however will not download to the cache folder if the package already exists in the current project node_modules folder, or if the package is globally installed.

To force NPX to download the latest version of a package and ignore any local or global installed version, we should specify the NPX flag --ignore-existing.


Common usages of NPX are:

npx create-react-app my-app
This will set the skeleton of a new react based application.

npx serve
This runs a file server on the current folder, enabling a quick review of the HTML files using a browser.


By default the NPX downloads the package, and the checks for "bin" entry in the package.json, which specifies the javascript file name to run. However we can manually determine the command to run using the syntax:

npx --package my-package my-command

In such case NPX would download the package and then look for the command under the bin element in package JSON and run it. Notice that we cannot run any javascript file using NPX, but only the predefined entries in the bin element. We can however, download the package using NPX, and the run any javascript file using node from the local NPX cache folder.


Monday, September 15, 2025

Create GO MCP Server and a Valid SSL Certificate

 



In this post we create GO bassed MCP server. 

The server includes both SSE and Streaming support. Gemini CLI supports only SSE, but the standard seems to be going to HTTM Streaming protocol.

We support both HTTP server on port 80, and HTTPS server on port 443. When using HTTPS server on port 443, we also start a certificate management listener on port 80 which will allocate a valid certificate as long as we have a valid DNS pointing to our server. Notice that for the SSL certification allocated we need to allow all source IPs to the port 80 listener, since the request will arrive from the Lets' encrypt servers.


package main

import (
"context"
"encoding/json"
"fmt"
"net/http"

"github.com/modelcontextprotocol/go-sdk/jsonschema"
"github.com/modelcontextprotocol/go-sdk/mcp"
"golang.org/x/crypto/acme/autocert"
)

func main() {
implementation := mcp.Implementation{
Name: "Demo MCP Server",
}
mcpServer := mcp.NewServer(&implementation, nil)

mcpServer.AddTool(toolSchema(), toolExecute)

useSse := false
var mcpHandler http.Handler
if useSse {
mcpHandler = mcp.NewSSEHandler(func(*http.Request) *mcp.Server {
return mcpServer
})
} else {
mcpHandler = mcp.NewStreamableHTTPHandler(func(request *http.Request) *mcp.Server {
return mcpServer
}, nil)
}

useTls := true
if useTls {
certManager := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("my.domain.com"),
Cache: autocert.DirCache("certs"),
}

httpServer := &http.Server{
Addr: "0.0.0.0:443",
Handler: mcpHandler,
TLSConfig: certManager.TLSConfig(),
}

go func() {
err := http.ListenAndServe("0.0.0.0:80", certManager.HTTPHandler(nil))
if err != nil {
panic(err)
}
}()

err := httpServer.ListenAndServeTLS("", "")
if err != nil {
panic(err)
}
} else {
err := http.ListenAndServe("0.0.0.0:80", mcpHandler)
if err != nil {
panic(err)
}
}
}


Now we implement the tool that can both read input parameters and return a string result for the AI agent to send to the LLM.


type inputParameters struct {
Name string `json:"name"`
}

func toolSchema() *mcp.Tool {
return &mcp.Tool{
Name: "greet",
Description: "Say hi from me",
InputSchema: &jsonschema.Schema{
Type: "object",
Required: []string{"name"},
Properties: map[string]*jsonschema.Schema{
"name": {
Type: "string",
},
},
},
}
}

func toolExecute(
_ context.Context,
_ *mcp.ServerSession,
params *mcp.CallToolParamsFor[map[string]any],
) (
*mcp.CallToolResultFor[any],
error,
) {
bytes, err := json.Marshal(params.Arguments)
if err != nil {
panic(err)
}

var input inputParameters
err = json.Unmarshal(bytes, &input)
if err != nil {
panic(err)
}

content := mcp.TextContent{
Text: fmt.Sprintf("Hi %v, Demo MCP is at your service", input.Name),
}

result := mcp.CallToolResultFor[any]{
Content: []mcp.Content{
&content,
},
}

return &result, nil
}



Monday, September 8, 2025

Create a Python MCP Server

 




MCP  is the standard protocol for exposing tools for AI agents usage. The MCP exposes APIs and the documentation for each API to the LLM. In this post we create a simple python based MCP server and use it in Gemini CLI.


Create The MCP Server


To prepare the MCP server project use:

# install UV in case it is not already installed
curl -LsSf https://astral.sh/uv/install.sh | sh

uv init magic_server
cd magic_server
uv venv
uv add "mcp[cli]"


Next we add our main.py and expose our tool:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Demo")

@mcp.tool(name="do_magic_trick")
def do_magic_trick(a: int, b: int) -> int:
return a + b + 6

mcp.run(transport="stdio")
#mcp.run(transport="streamable-http")
#mcp.run(transport="sse")


We can run the MCP as a local tool using STDIO for the lower level communication, or using SSE and streamable-http to connect to the server over the network.


Run Gemini CLI and MCP Server STDIO


To use gemini CLI, make sure you have recent version of node, and run:

npx https://github.com/google-gemini/gemini-cli

Run the MCP as a local tool using the STDIO. To configure this, update the file ~/.gemini/settings.json


{
"selectedAuthType": "oauth-personal",
"mcpServers": {
"pythonTools": {
"command": "/home/my-user/.local/bin/uv",
"args": [
"run",
"main.py"
],
"cwd": "/home/my-user/magic_server",
"env": {
},
"timeout": 15000
}
}
}

Once we restart the gemini CLI it will run the MCP server to get the metadata of the exposed tools, and we can use the tools simple by using the prompt: "do the magic_trick for 5 and 6".


Run Gemini CLI and MCP Server Service

To expose the MCP server as a public tool, we need to run the MCP server over the network. For this we can use SSE and http-streaming, so update the main.py to use the relevant method.


Notice:

1. At this time gemini CLI supports only SSE, but it seems that http-streaming is the winning standard.

2. In case using TLS (HTTPS access) the server should use a valid TLS certificate.


To configure the gemini to use the tool over the network, update the file ~/.gemini/settings.json:

{
"selectedAuthType": "oauth-personal",
"mcpServers": {
"discoveredServer": {
"url": "http://localhost:8000/sse"
}
}
}


Unlike running MCP using the STDIO, in this case we need to run the MCP ourselves:

uv run main.py