mcp-extensions

command
v2.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 11 Imported by: 0

README

MCP application extensions

This example registers an in-memory blog domain as MCP tools and a resource template. Read mcp-basic first if you only need the transport shape.

The example contrasts two tool APIs:

  • NewTypedTool derives input and output schemas from Go types and validates tagged arguments before calling the handler;
  • NewTool gives the application direct control over a schema and a map[string]any handler.

Prefer typed tools for normal domain operations. Use the builder when a schema cannot be expressed by the typed generator.

Register the domain

Each tool is one operation on the store. The extension keeps related tools and resources together:

ext := mcp.NewExtension("blog").
	WithTool(mcp.NewTypedTool("create_post", "Create a post.", store.Create)).
	WithTool(mcp.NewTypedTool("get_post", "Fetch a post.", store.Get)).
	WithTool(store.searchTool()).
	WithResourceTemplate(postResourceTemplate{store: store}).
	Build()

if err := app.RegisterMCPExtension(ext); err != nil {
	log.Fatal(err)
}

The typed handler uses ordinary Go input and output types:

type CreatePostArgs struct {
	Title  string `json:"title" validate:"required,max=200"`
	Author string `json:"author" validate:"required"`
}

func (b *blog) Create(ctx context.Context, args CreatePostArgs) (Post, error)

blog://posts/{id} is a resource template rather than a tool because reading a post has no side effect. Its Subscribe method emits invalidations; the client then reads the resource again for the current value.

Run it

From the repository root:

go run ./examples/mcp-extensions

From another terminal, list the registered tools using the current Streamable HTTP request metadata:

curl -sS -X POST http://localhost:8080/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -H 'Mcp-Method: tools/list' \
  -d '{
    "jsonrpc":"2.0",
    "method":"tools/list",
    "params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28"}},
    "id":1
  }'

For tools/call, send the same protocol metadata, set Mcp-Method to tools/call, and set Mcp-Name to create_post. See the MCP guide for complete tool calls and resource subscriptions.

This program has no authentication and stores data only in memory. Keep it on a development listener; a real service must authenticate /mcp, authorize each operation, and use durable storage where required.

Documentation

Overview

Example: typed MCP tools — one tool per verb.

Each tool is a thin wrapper around a method on the blog store. The args struct says exactly what the tool needs (no "optional unless action=…" gymnastics), the return type is a concrete domain object that gets JSON-marshaled and advertised via `outputSchema` on tools/list, and every handler reads like normal Go — no map[string]any assertions.

One tool is kept on the older builder API (search_posts) so the two shapes stay visible side by side. Use the typed shape for new tools and the builder when you need to hand-tune a schema the typed generator doesn't emit yet.

The example also registers a subscribable resource template: blog://posts/{id}. `resources/templates/list` advertises the family, `resources/read` resolves concrete post IDs, and SSE/stdio clients can subscribe for standard `notifications/resources/updated` invalidations.

Try it:

go run ./examples/mcp-extensions &

# tools/list now carries inputSchema AND outputSchema for typed tools.
curl -s -X POST localhost:8080/mcp -H 'Content-Type: application/json' \
     -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq .

curl -s -X POST localhost:8080/mcp -H 'Content-Type: application/json' \
     -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{
            "name":"create_post",
            "arguments":{"title":"hello","author":"ada","content":"first","tags":["intro"]}
          }}'

# Validation: required field missing surfaces through the MCP error envelope.
curl -s -X POST localhost:8080/mcp -H 'Content-Type: application/json' \
     -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{
            "name":"create_post","arguments":{"title":"hello"}
          }}'

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL