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.
The server binds 127.0.0.1 by default and has no authentication. See
Security before binding any other address.
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.
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:
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
| Option | Required | Description |
|---|---|---|
--profile | No | Profile to serve. Default: the default profile (the built-in one unless overridden) |
--host | No | Host to bind. Overrides the profile's host. Default 127.0.0.1 |
--port | No | Port to bind. Overrides the profile's port. Default 5100 |
--allow-remote | No | Required to bind a non-loopback host. Without it the server refuses to start |
--host or --port flag, then the value saved in the
profile, then 127.0.0.1 and 5100. See Profiles.
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.
Saved MCP profile 'text-tools' (6 tools)
| Option | Required | Description |
|---|---|---|
--name | Yes | Profile name (lowercase letters, digits and hyphens) |
--include | Yes | Entries to expose: topic or topic/action[/sub]. Repeatable, and also accepts several values after one flag |
--host | No | Host to bind when serving this profile |
--port | No | Port to bind when serving this profile |
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.
Deleted MCP profile 'text-tools'
| Option | Required | Description |
|---|---|---|
--name | Yes | Profile 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).
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:
Saved MCP profile 'default' (5 tools)
Restoring the default
Deleting the default profile brings back the built-in one:
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:
interpreters brainfuckbecomesinterpreters_brainfuckinterpreters bf-to-ookbecomesinterpreters_bf-to-ookstrings levenshtein-distancebecomesstrings_levenshtein-distance
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 type | JSON schema type |
|---|---|
| string | string |
| int, long | integer |
| double, float, decimal | number |
| bool | boolean |
| array or collection | array of string |
| enum | string 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:
- The command's stdout.
- A
stderr:section with the command's stderr, if there is any. - An
exit code: Nline, 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:
- Values starting with
@are literal. System.CommandLine response-file expansion is disabled for tool calls, so an argument value such as@notes.txtis passed through as that text. The command line is unchanged: there,@filestill expands a response file. - Stdin is empty. Inside a tool call, a
-"read stdin" file argument reads nothing. - Timeouts stop the interpreters. The Brainfuck and Ook interpreters stop when the call times out; the 300 second timeout cancels them.
- Argument errors end with an exit code line. An unknown argument or an unsupported type produces an
error result that ends with
exit code: 1, like the error result of a command that fails. A call to a tool name the profile does not contain is different: its whole text isUnknown tool '<name>'., with no exit code line, because no command runs. - Every call is logged. Every call, including an unknown tool and a call the client cancelled, writes
one
tool <name> exit <code> <ms>msline to the server's stderr.
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:
mcp(all of it)config(all of it)web serve-htmldemoscene(all of it)oauth token,oauth profile addandoauth profile update
oauth profile delete and the other oauth actions are not in this list, so they can be exposed.
Security
- Loopback by default. The server binds
127.0.0.1. Pass--hostor save a host in a profile to bind somewhere else. - No authentication. Anyone who can reach the port can call every exposed tool. The tools read and write files as the user who runs the server.
- Host and Origin checks on loopback. While bound to a loopback address, a request whose
Hostheader is notlocalhost,127.0.0.1or[::1]gets403 Forbidden. So does a request with anOriginheader that is not loopback. - Non-loopback binds need
--allow-remote. Without the flag the server refuses to start on another address. With it, a warning is printed to stderr:
Warning: binding to non-loopback address 0.0.0.0; the MCP server has no authentication.
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 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 |