MCP mcp

Serve a profile of toolkit commands as MCP tools over stateless HTTP, executed in-process
Pixelbadger Toolkit — pbtk mcp — ← all topics

Overview

The mcp topic serves a profile of toolkit commands as MCP tools over stateless HTTP. Each command in the profile becomes one tool. A tool call runs the command in the server process and returns its output as the tool result. A profile is a named list of topics, groups or actions, saved per user.

$ pbtk mcp <action> [options]

The server binds 127.0.0.1 by default and has no authentication. See Security before binding any other address.

Profiles are read when serve starts. Changing a profile does not affect a server that is already running.

serve

Serves a profile at /mcp. Without --profile it serves the default profile, which is the whole interpreters topic unless you save a profile named default. The server prints its URL and tool list at startup, then runs until it is stopped.

$ pbtk mcp serve
Serving MCP profile 'default' on http://127.0.0.1:5100/mcp
Tools: interpreters_brainfuck, interpreters_ook, interpreters_bf-to-ook

To serve a saved profile on another port:

$ pbtk mcp serve --profile text-tools --port 5200

Each tool call writes one line to stderr when it finishes, in the form tool <name> exit <code> <ms>ms:

tool interpreters_brainfuck exit 0 60ms
OptionRequiredDescription
--profileNoProfile to serve. Default: the default profile (the built-in one unless overridden)
--hostNoHost to bind. Overrides the profile's host. Default 127.0.0.1
--portNoPort to bind. Overrides the profile's port. Default 5100
--allow-remoteNoRequired to bind a non-loopback host. Without it the server refuses to start
Precedence for the bind address is: the --host or --port flag, then the value saved in the profile, then 127.0.0.1 and 5100. See Profiles.
Errors. An unknown profile prints Error: MCP profile 'nope' not found. A profile that exposes no tools prints Error: MCP profile '<name>' exposes no tools. Both exit with status 1.

save

Saves a named profile. The profile name must match ^[a-z0-9-]+$. Saving a name that already exists overwrites it without a prompt. Each --include entry is either a topic (strings) or an action path (interpreters/brainfuck). The command rejects an entry that does not exist or cannot be exposed, and exits with status 1.

$ pbtk mcp save --name text-tools --include strings interpreters/brainfuck --port 5200
Saved MCP profile 'text-tools' (6 tools)
OptionRequiredDescription
--nameYesProfile name (lowercase letters, digits and hyphens)
--includeYesEntries to expose: topic or topic/action[/sub]. Repeatable, and also accepts several values after one flag
--hostNoHost to bind when serving this profile
--portNoPort to bind when serving this profile
Non-exposable entries are rejected. mcp, config, web serve-html, demoscene, oauth token, oauth profile add and oauth profile update cannot be saved. For example, pbtk mcp save --name x --include config prints Error: Unknown or non-exposable entry 'config'. and exits with status 1. Names that do not match the pattern print Error: Invalid profile name '<name>'. Profile names must match ^[a-z0-9-]+$.

delete

Deletes a saved profile. Deleting a profile that does not exist exits with status 1. Deleting the profile named default restores the built-in default. See Profiles.

$ pbtk mcp delete --name text-tools
Deleted MCP profile 'text-tools'
OptionRequiredDescription
--nameYesProfile name

list

Lists the built-in default profile, unless a profile named default is saved, and every saved profile in name order. Each profile shows its entries, host and port. An entry that no longer resolves is marked (unresolved).

$ pbtk mcp list
default (built-in)
  include: interpreters
  host: (default)
  port: (default)

text-tools
  include: strings, interpreters/brainfuck
  host: (default)
  port: 5200

Profiles

Profiles are stored in the same per-user config.db as config, in a table named mcp_profiles.

Entry syntax

An entry is topic or topic/action[/sub]. It resolves to every command at or below that node that can be exposed as a tool. For example, strings exposes all five strings actions, and interpreters/brainfuck exposes one tool.

The built-in default

With no saved default profile, the built-in profile is interpreters, which exposes interpreters_brainfuck, interpreters_ook and interpreters_bf-to-ook.

Overriding the default

Saving a profile named default replaces the built-in one:

$ pbtk mcp save --name default --include strings
Saved MCP profile 'default' (5 tools)

Restoring the default

Deleting the default profile brings back the built-in one:

$ pbtk mcp delete --name default
Deleted MCP profile 'default'
Built-in default profile restored.

Overwriting a profile

save always overwrites. Saving text-tools again replaces its entries, host and port with the new values. Nothing is merged.

Stale entries

If an entry in a saved profile no longer resolves, for example after a command is renamed, serve prints a warning to stderr and skips the entry. The other entries are still served. If no entry resolves, the server does not start:

Warning: skipping unresolved entry 'strings/nope' in MCP profile 'text-tools'

Host and port

The address the server binds is chosen in this order: the --host or --port flag of serve, then the host or port saved in the profile, then 127.0.0.1 and 5100. The endpoint path is always /mcp.

pbtk mcp save --name text-tools --include strings --port 5200 followed by pbtk mcp serve --profile text-tools binds 5200. Adding --port 5201 to the serve command binds 5201 instead.

Tools

Naming

A tool's name is the action path joined with _. Its description is the command's description. For example:

Input schema

Each property is one option, named by the option name without its leading dashes. For example, --in-file becomes the property in-file. The JSON type follows the option's type:

Option typeJSON schema type
stringstring
int, longinteger
double, float, decimalnumber
boolboolean
array or collectionarray of string
enumstring with the enum's names as allowed values

A required option is listed in required. Any property that is not in the schema is an error. For example, interpreters_brainfuck takes one required string property, file:

{ "name": "interpreters_brainfuck", "arguments": { "file": "hello.bf" } }

Arguments are turned back into CLI tokens, so interpreters_brainfuck with file set to hello.bf runs interpreters brainfuck --file hello.bf. A boolean is passed explicitly as --flag true or --flag false.

Results

A tool result is a single text block, built in this order:

  1. The command's stdout.
  2. A stderr: section with the command's stderr, if there is any.
  3. An exit code: N line, if the exit code is not 0.

If the command produced no output at all, the text is (no output). The result has isError set when the exit code is not 0. For example, a missing input file returns:

Error: Brainfuck program file not found: /nonexistent/x.bf
exit code: 1

An argument the schema does not allow, such as an unknown property or an unsupported type, returns an error result like a failing command. For example, an unknown property returns:

Unknown argument 'nope' for tool interpreters_brainfuck.
exit code: 1

Call behaviour

These rules apply to every tool call, and differ from the command line in the first two cases:

Concurrency and timeout

Tool calls run concurrently. Each call uses its own command tree and captures its own output. A call that runs longer than 300 seconds is cancelled and returns an error result, Tool <name> timed out after 300 s.

Your config defaults

Tool calls use the same config path as the command line. Defaults set with config apply to them, and a value given in the arguments overrides the default.

Never exposed

These commands are never exposed as tools, and mcp save rejects them:

oauth profile delete and the other oauth actions are not in this list, so they can be exposed.

Security

Warning: binding to non-loopback address 0.0.0.0; the MCP server has no authentication.
Do not bind a non-loopback address on a network you do not trust. The Host and Origin checks apply only to loopback binds, and the server has no authentication in any case. A tool that times out is cancelled cooperatively; a command that ignores cancellation (such as llm or gpt train) can keep running and writing files.

Connecting a client

Start a server, then add it to Claude Code as an HTTP MCP server. Use the port of the profile you serve:

$ pbtk mcp serve
$ claude mcp add --transport http pbtk http://127.0.0.1:5100/mcp
pbtk config mcp serve --profile <name> sets the profile that serve uses when you do not pass --profile. --host and --port cannot be configured, and neither can the options of save and delete.

Command Reference

Command Required options Optional options Output
mcp serve — --profile --host --port Startup URL and tool list on stdout, one stderr line per tool call
mcp save --name --include --host --port Saved MCP profile '<name>' (N tools)
mcp delete --name — Deleted MCP profile '<name>'
mcp list — — Each profile with its entries, host and port