Back to skills
SKILL.md
Apx Mcp Builder
ASecurityAuthor a new MCP (Model Context Protocol) server and register it in APX. Covers JSON-RPC stdio protocol, FastMCP (Python) / TypeScript SDK shapes, tool design, secrets, debugging. Load when building a new MCP / exposing a new tool surface. To add an existing MCP, load `apx-mcp` instead.
- 12 stars
- 0 votes
- 0 copies
- 0 views
- Added September 27, 2026
Works with
Security analysis
96/100- Installs packages at runtime which could introduce malicious dependencies
npx -y skills add agentprojectcontext/apx --skill apx-mcp-builder --agent claude-codeAre you the author of Apx Mcp Builder?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/agentprojectcontext-apx-mcp-builder)---
name: apx-mcp-builder
scope: internal
description: Author a new MCP (Model Context Protocol) server and register it in APX. Covers JSON-RPC stdio protocol, FastMCP (Python) / TypeScript SDK shapes, tool design, secrets, debugging. Load when building a new MCP / exposing a new tool surface. To add an existing MCP, load `apx-mcp` instead.
---
# apx-mcp-builder
An MCP server is a tiny stdio JSON-RPC process. APX spawns it (via `apx mcp add`), discovers tools (`tools/list`), and lets the super-agent call them by name.
## Minimum surface
| Method | Purpose | Returns |
|---|---|---|
| `initialize` | Handshake (protocol version + capabilities). | Server capabilities. |
| `tools/list` | Enumerate tools. | `{ tools: [{name, description, inputSchema}] }` |
| `tools/call` | Run one tool with `{ name, arguments }`. | `{ content: [...], isError? }` |
| `notifications/initialized` | One-way init-done from APX. | — |
JSON-RPC 2.0 over stdio, framed `Content-Length: N\r\n\r\n<JSON>`. The `@modelcontextprotocol/sdk` (TS) and `fastmcp` / `mcp` (Python) packages handle framing.
## Pick a stack
| Stack | When |
|---|---|
| **Python + FastMCP** | Fastest path; decorator-based; ideal for pip-wrapping tools. |
| **Node + @modelcontextprotocol/sdk** | Node-only libs, or zero install (`npx -y`). |
| Raw JSON-RPC | Only when exposing an existing daemon. Otherwise use the SDK. |
## Python (FastMCP) — minimum viable server
```python
# my_server.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-server")
@mcp.tool()
def search_inventory(query: str, limit: int = 10) -> list[dict]:
"""Search the inventory by free-text query. Returns matching SKUs."""
return [{"sku": "abc", "title": "..."}]
@mcp.tool()
def get_stock(sku: str) -> dict:
"""Return the current stock count for a SKU."""
return {"sku": sku, "stock": 42}
if __name__ == "__main__":
mcp.run() # stdio transport
```
```bash
pip install fastmcp
# or: uv tool install fastmcp
apx mcp add my-server \
--command uv --project acme \
-- run python /abs/path/my_server.py
```
## Node (TypeScript SDK) — minimum viable server
```typescript
// my-server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
ListToolsRequestSchema,
CallToolRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "my-server", version: "0.1.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "search_inventory",
description: "Search the inventory by free-text query.",
inputSchema: {
type: "object",
properties: {
query: { type: "string" },
limit: { type: "number", default: 10 },
},
required: ["query"],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (req) => {
if (req.params.name === "search_inventory") {
const { query, limit = 10 } = req.params.arguments as any;
return {
content: [{ type: "text", text: JSON.stringify([{ sku: "abc" }]) }],
};
}
throw new Error(`unknown tool: ${req.params.name}`);
});
await server.connect(new StdioServerTransport());
```
```bash
apx mcp add my-server \
--command npx --project acme \
-- -y my-server-package
```
## Tool design rules
1. **Names are snake_case verbs**: `search_inventory`, not `inventorySearch`.
2. **inputSchema is JSON Schema draft-07**. `required` matters — vague schemas → bad calls.
3. **Descriptions read like a man-page**. Tool + param descriptions are surfaced to the model.
4. **Return structured data**, not prose. Convention: JSON inside a text content block. Images use the `image` type.
5. **Errors are errors**: `throw new Error(...)` propagates as `isError: true`. Empty result = "found nothing" — different signal.
## Env vars and secrets
```bash
apx mcp add github \
--scope runtime --project acme \
--command npx \
--env GITHUB_TOKEN=ghp_xxx \
--env GITHUB_OWNER=acme \
-- -y @modelcontextprotocol/server-github
```
`--scope runtime` writes to `~/.apx/projects/<id>/mcps.json` (chmod 0600, never committed). NEVER `--scope shared` for tokens — that file lives in `.apc/` and gets committed.
## Anti-examples
```python
# DON'T expose every internal function. A server with 40 tools eats context.
# Pick 5-10 high-value tools per server; split into multiple servers if more.
# DON'T return giant blobs in `content`. The agent re-reads every turn.
# Paginate (`offset`, `limit`) and let the agent ask for more.
# DON'T mix stdio + HTTP transports in one process. APX spawns stdio;
# for HTTP use `url:` in mcps.json instead of `command:`.
# DON'T print to stdout outside JSON-RPC frames. Stray print() corrupts
# the protocol — use stderr for logs. (SDKs handle this; bare `print("debug")`
# in a tool body breaks everything.)
```
## Debugging
```bash
# Smoke test — spawn the server and list its tool catalog
apx mcp tools my-server
# Inspect one tool's schema + copy-paste run example
apx mcp tools my-server search_inventory
# Call it for real
apx mcp run my-server search_inventory '{"query":"shoes"}'
# Spawn errors / stderr tail
apx mcp logs my-server
apx log -f
# Scopes / files / env APX sees
apx mcp check --project acme
```
## Don't
- Don't reinvent SDK framing — bypass only with reason.
- Don't make interactive tools. MCP is one-shot per call; split flows into multiple tools.
- Don't ship without a README — future-you forgets which env vars matter.
- Don't expect APX to retry MCP errors. The agent sees the error and decides.
Attribution
Comments
Loading comments…