feat: mirror sweetistics mcp workflow

This commit is contained in:
Peter Steinberger 2025-11-05 04:08:01 +00:00
parent 5744ce370f
commit cfa2b8df50
7 changed files with 190 additions and 10 deletions

View File

@ -57,6 +57,10 @@ npx mcp-runtime list # list all configured servers
npx mcp-runtime list vercel --schema # show tool signatures + schemas
npx mcp-runtime call linear.searchIssues owner=ENG status=InProgress
npx mcp-runtime call signoz.query --tail-log # print the tail of returned log files
# local scripts mirroring the Sweetistics workflow
pnpm mcp:list # alias for mcp-runtime list
pnpm mcp:call chrome-devtools.getTabs --tail-log
```
Common flags:

84
config/mcp_servers.json Normal file
View File

@ -0,0 +1,84 @@
[
{
"name": "chrome-devtools",
"description": "Chrome DevTools protocol bridge for driving local tabs during debugging or automation.",
"command": [
"bash",
"scripts/mcp_stdio_wrapper.sh",
"env",
"npm_config_loglevel=error",
"npx",
"-y",
"chrome-devtools-mcp@latest"
]
},
{
"name": "context7",
"description": "Context7 MCP for React documentation lookup.",
"command": "https://mcp.context7.com/mcp"
},
{
"name": "linear",
"description": "Hosted Linear MCP; exposes issue search, create, and workflow tooling.",
"command": "https://mcp.linear.app/mcp",
"headers": {
"Authorization": "Bearer ${LINEAR_API_KEY}"
}
},
{
"name": "next-devtools",
"description": "Next.js dev server introspection (project metadata, logs, page insights).",
"command": "http://localhost:3000/_next/mcp"
},
{
"name": "playwright",
"description": "Playwright MCP server for accessibility-driven automation.",
"command": [
"bash",
"scripts/mcp_stdio_wrapper.sh",
"env",
"npm_config_loglevel=error",
"npx",
"-y",
"@playwright/mcp@latest"
]
},
{
"name": "vercel",
"description": "Vercel MCP (requires OAuth).",
"command": "https://mcp.vercel.com",
"auth": "oauth",
"token_cache_dir": "~/.cache/mcp-runtime/vercel"
},
{
"name": "firecrawl",
"description": "Firecrawl hosted MCP for authenticated scraping and extraction.",
"command": "https://mcp.firecrawl.dev/v2/mcp",
"headers": {
"Authorization": "Bearer ${FIRECRAWL_API_KEY}"
}
},
{
"name": "signoz",
"description": "SigNoz Query MCP server (logs, traces, metrics).",
"command": [
"bash",
"scripts/mcp_stdio_wrapper.sh",
"env",
"NODE_OPTIONS=--require=./scripts/mcp_signoz_retry_patch.cjs",
"npm_config_loglevel=error",
"npx",
"-y",
"signoz-mcp-server@latest"
],
"env": {
"SIGNOZ_URL": "${SIGNOZ_URL:-http://localhost:3301}",
"SIGNOZ_TOKEN": "${SIGNOZ_TOKEN:-}"
}
},
{
"name": "shadcn",
"description": "shadcn/ui registry MCP for browsing component recipes.",
"command": "https://www.shadcn.io/api/mcp"
}
]

52
docs/mcp.md Normal file
View File

@ -0,0 +1,52 @@
---
summary: 'Working with configured MCP servers via the mcp-runtime CLI and scripts.'
---
# MCP Workflow
`mcp-runtime` reads server definitions from `config/mcp_servers.json`. Each entry mirrors the Sweetistics setup (headers, stdio wrappers, OAuth hints) so team members can lean on the same command surface area without Python dependencies.
## Scripts
```
pnpm mcp:list [<name>] [--schema]
pnpm mcp:call <server>.<tool> [key=value...] [--args '{"foo":"bar"}'] [--tail-log]
```
Both scripts forward straight to the TypeScript CLI (`src/cli.ts`), so they support the same flags documented in the [README](../README.md).
- `pnpm mcp:list` enumerate all configured servers; pass a specific name to inspect tool signatures.
- `pnpm mcp:list <name> --schema` dump the full JSON schema for each tool exposed by `<name>`.
- `pnpm mcp:call` execute a tool using either loose `key=value` pairs or `--args` JSON; append `--tail-log` to follow log files reported by the response.
## Adding or Updating Servers
1. Edit `config/mcp_servers.json` (keep the entries sorted alphabetically).
2. Use `${ENV}` or `${ENV:-default}` placeholders for secrets; they are resolved at runtime.
3. Set `auth: "oauth"` when the server requires an OAuth dance the CLI spins up a local callback server and persists tokens under `~/.mcp-runtime/<name>/`.
4. For stdio transports, wrap the command with `scripts/mcp_stdio_wrapper.sh` to inherit repo-relative paths, just like the Sweetistics helper.
After editing the config, you can validate the entry with:
```
pnpm mcp:list <name>
pnpm mcp:call <name>.<tool> --args '{"sample":true}'
```
## Environment Variables
The CLI respects the same conventions as the original Python wrapper:
- `${LINEAR_API_KEY}`, `${FIRECRAWL_API_KEY}`, etc. for hosted servers.
- `$env:VAR` to inject the raw runtime value without fallbacks.
- `env` blocks per entry to provide default values (e.g., SigNoz URLs/tokens).
## Troubleshooting
| Issue | Suggested Fix |
| --- | --- |
| OAuth flow never completes | Ensure the browser opened `http://127.0.0.1:<port>/callback`; copy the printed URL manually if not. |
| Stdio command cannot find scripts | Pass `--root <repo>` or run from the project root so relative paths resolve. |
| Tokens are stale | Delete `~/.mcp-runtime/<server>/tokens.json` and rerun the command. |
Refer to [`docs/spec.md`](./spec.md) for deeper architectural notes and future roadmap items.

View File

@ -21,12 +21,17 @@
},
"scripts": {
"build": "tsc -p tsconfig.build.json",
"check": "biome check",
"check": "pnpm lint:biome && pnpm lint:oxlint && pnpm typecheck",
"lint": "pnpm check",
"lint:biome": "biome check",
"lint:oxlint": "oxlint --type-aware --tsconfig tsconfig.json --max-warnings=0",
"typecheck": "tsgo --project tsconfig.json --noEmit",
"test": "vitest run",
"clean": "rimraf dist",
"dev": "tsc -w -p tsconfig.build.json",
"prepublishOnly": "pnpm check && pnpm test && pnpm build"
"prepublishOnly": "pnpm check && pnpm test && pnpm build",
"mcp:list": "pnpm exec tsx src/cli.ts list",
"mcp:call": "pnpm exec tsx src/cli.ts call"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.10.1",
@ -36,8 +41,12 @@
"@biomejs/biome": "^2.3.3",
"@types/express": "^4.17.21",
"@types/node": "^22.7.4",
"@typescript/native-preview": "7.0.0-dev.20251104.1",
"express": "^4.21.1",
"oxlint": "^1.25.0",
"oxlint-tsgolint": "^0.5.0",
"rimraf": "^6.0.1",
"tsx": "^4.16.5",
"typescript": "^5.6.3",
"vitest": "^1.6.0"
},

View File

@ -0,0 +1,9 @@
// No-op hook retained for compatibility with the Sweetistics configuration.
// Signoz MCP occasionally retries on transient errors; keeping this module
// allows `NODE_OPTIONS=--require=./scripts/mcp_signoz_retry_patch.cjs` to load
// without throwing if teams reuse the same entry.
module.exports = {
// Export a no-op hook so consumers can keep requiring this patch file safely.
onRetry() {},
};

9
scripts/mcp_stdio_wrapper.sh Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -eq 0 ]]; then
echo "mcp_stdio_wrapper.sh: missing command" >&2
exit 1
fi
exec "$@"

View File

@ -12,6 +12,9 @@ import { createRuntime } from "../src/runtime.js";
const app = express();
app.use(express.json());
app.get("/mcp", (_req, res) => {
res.sendStatus(405);
});
const server = new McpServer({
name: "integration-demo",
@ -42,18 +45,28 @@ server.registerResource(
title: "Greeting",
description: "Dynamic greeting resource",
},
async (uri, { name }) => ({
contents: [
{
uri: uri.href,
text: `Hello, ${name}!`,
},
],
}),
async (uri, { name }) => {
const normalizedName =
typeof name === "string"
? name
: Array.isArray(name)
? name.join(", ")
: "friend";
return {
contents: [
{
uri: uri.href,
text: `Hello, ${normalizedName}!`,
},
],
};
},
);
app.post("/mcp", async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true,
});