Add or change an MCP tool or server on this repo's shared mcp-common scaffolding — the McpTool SPI, transport-neutral ToolDefinition/ToolResult, the three transports, path confinement, MCP_USAGE.md and the *IT tests. Use when exposing functionality over MCP, wiring a new server, or when the user says "MCP tool", "MCP server", "stdio transport", or "streamable HTTP".
Scanned 8/31/2026
Install to Claude Code
npx -y skills add adamw7/tools --skill mcp-server --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Mcp Server?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/adamw7-mcp-server)More formats (shields.io, HTML) on the badges page.
---
name: mcp-server
description: Add or change an MCP tool or server on this repo's shared mcp-common scaffolding — the McpTool SPI, transport-neutral ToolDefinition/ToolResult, the three transports, path confinement, MCP_USAGE.md and the *IT tests. Use when exposing functionality over MCP, wiring a new server, or when the user says "MCP tool", "MCP server", "stdio transport", or "streamable HTTP".
---
# MCP Server Skill
Expose functionality over the Model Context Protocol using `mcp-common`, the
shared scaffolding behind all three servers here (`data` uniqueness,
`code/context`, `adopt`). Transport, registration and error handling are already
solved — a new tool writes none of that.
## The SPI — a tool never mentions the MCP SDK
```java
public class MyTool implements McpTool { // Function<Map<String,Object>, ToolResult>
@Override
public ToolDefinition getToolDefinition() {
return new ToolDefinition("my_tool", "What it does, for the model.",
Map.of("type", "object",
"properties", Map.of("path", Map.of("type", "string")),
"required", List.of("path")));
}
@Override
public ToolResult apply(Map<String, Object> arguments) {
String path = ToolArguments.requiredString(arguments, "path");
return ToolResult.success(answer); // or ToolResult.error(message)
}
}
```
- `ToolDefinition` (name, description, JSON-schema map) and `ToolResult`
(`success` / `error`) are the SPI's **own transport-neutral types**;
`AbstractMcpConfiguration` translates them into the SDK's `Tool` and
`CallToolResult`. Do not import `io.modelcontextprotocol` from a tool.
- Parse arguments with `ToolArguments`: `requiredString`, `requiredInt`,
`optionalString`, `optionalBoolean`, `optionalInt`, `optionalBoundedInt` —
don't hand-roll casts out of the map.
- **Don't catch-and-wrap for the protocol's sake.** `AbstractMcpConfiguration`
turns any thrown `RuntimeException` into an error `ToolResult` naming the
tool, so a bad argument reaches the client as an actionable tool error. Throw
a clear exception; return `ToolResult.error` when the failure is expected.
- **The failure path masks both channels** (`FailureMessage`): the arguments it
logs lose their credentials, and the message the client reads loses both those
and any absolute path, keeping only the file's name. Write exception messages
that stay useful under that — name what was wrong with the argument, not where
on the server's disk it resolved to.
## The server
```java
@Configuration
public class McpConfiguration extends AbstractMcpConfiguration {
@Override protected String serverName() { return "my-server"; }
@Override protected List<McpTool> tools() { return List.of(new MyTool()); }
}
```
That is the whole server. The base class supplies all three transports, selected
with `--transport.mode`:
| Mode | Value | Endpoint |
|---|---|---|
| stdio (default) | `stdio` | — |
| streamable HTTP | `streamable-http` | `/mcp` |
| stateless HTTP | `stateless-http` | `/mcp`, no session kept |
`TransportConfigurer.configure(args)` reads the argument through `TransportMode`,
so anything but those three values is refused at startup with a message naming
them. Write the conditions with `TransportMode.Value.*` rather than a literal:
an unmatched `havingValue` registers no transport, and outside stdio it also
leaves the web server on — a server that binds its port and serves no `/mcp`.
A `Main.java` Spring Boot entry point sits next to the configuration
(`entryPointsAreNamedMain` is an ArchUnit rule in `adopt`).
## Rules that bite
- **Confine file access.** A server that reads paths from a client must clamp
them to a configured base directory before returning any tool — the uniqueness
server builds an `AllowedPaths.under(baseDir)` in `tools()` and hands it to the
tool, defaulting to the working directory, so a client cannot steer it at
`/etc/passwd`. Give the boundary to the thing being confined, never to the JVM:
a process-wide setter confines every other source in the host too, and the
second server to start silently moves the first one's boundary.
- **The core must not depend on its MCP adapter.** ArchUnit pins this in every
module: the uniqueness/context/adoption logic knows nothing about `mcp`, and
only the `mcp` package may see Spring or the scaffolding. Put the adapter in
`…/mcp`, keep the logic outside it.
- Log through log4j2 — no `System.out`, which would corrupt the stdio transport
anyway.
## Document and test it
- Every server has an `MCP_USAGE.md` **next to its `mcp` package**, listing each
tool, its parameters and example calls. Update it in the same change as the
tool — it is the file users configure their client from.
- The servers are covered by `*IT` tests over real HTTP, gated behind the
`integration-tests` profile: `mvn -P integration-tests verify`. The profile is
declared **per module** (`data`, `code/context`, `adopt`,
`claude-code-enforcer`) — a new module's `*IT`s do not run until that module
gets its own copy.
- Each server's transport tests follow one shape: an abstract fixture holding the
client, the helpers and whatever both transports must assert, plus a
`McpStreamableHttpIT` and a `McpStatelessHttpIT` that supply only the transport
and the client name. Copy it for a new server, and pick a payload the
integration runner can actually serve — `adopt`'s pair *verifies* a repository
rather than adopting one, because no runner has a logged-in `claude`.
- Unit-test the tool directly as a function: build the argument map, assert on
the `ToolResult` text and `isError`.
## References
- `mcp-common/.../AbstractMcpConfiguration.java` — transports and registration
- `data/.../uniqueness/mcp/` — the smallest complete example
- `docs/adr/0009-mcp-servers-on-spring-boot.md` — why Spring Boot
- The three `MCP_USAGE.md` files — the user-facing contract
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!