Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish to npm
name: Publish to npm and MCP Registry

on:
release:
Expand Down Expand Up @@ -38,3 +38,45 @@ jobs:
- run: npm test
- run: npm pack --dry-run --ignore-scripts
- run: npm publish --access public

publish-registry:
name: Publish MCP Registry metadata
needs: publish
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name }}
- uses: actions/setup-node@v4
with:
node-version: "22"
- name: Set registry version from release tag
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
node --input-type=module <<'JS'
import fs from 'node:fs';
const server = JSON.parse(fs.readFileSync('server.json', 'utf8'));
const version = process.env.RELEASE_TAG.slice(1);
server.version = version;
server.packages.forEach(pkg => { pkg.version = version; });
fs.writeFileSync('server.json', JSON.stringify(server, null, 2) + '\n');
JS
- name: Install mcp-publisher
env:
MCP_PUBLISHER_VERSION: "1.8.1"
MCP_PUBLISHER_SHA256: "a06c9096dcb9727c13555b6be26c7effa707b01f06a4c561ba7a3635443cf2cc"
run: |
publisher_dir="$RUNNER_TEMP/mcp-publisher"
mkdir -p "$publisher_dir"
curl --fail --location --retry 3 \
"https://github.com/modelcontextprotocol/registry/releases/download/v${MCP_PUBLISHER_VERSION}/mcp-publisher_linux_amd64.tar.gz" \
--output "$publisher_dir/archive.tar.gz"
printf '%s %s\n' "$MCP_PUBLISHER_SHA256" "$publisher_dir/archive.tar.gz" | sha256sum --check --strict
tar -xzf "$publisher_dir/archive.tar.gz" -C "$publisher_dir" mcp-publisher
echo "$publisher_dir" >> "$GITHUB_PATH"
- name: Authenticate to MCP Registry
run: mcp-publisher login github-oidc
- name: Publish server to MCP Registry
run: mcp-publisher publish server.json
5 changes: 4 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,7 @@ bash .github/scripts/run-fmsg-docker-e2e.sh # real stacks via fmsg-docke
## Releasing

Tag `vX.Y.Z`, publish a GitHub release; `.github/workflows/publish.yml` sets the version from the
tag, runs the checks and publishes to npm with OIDC trusted publishing.
tag, runs the checks and publishes to npm with OIDC trusted publishing. After npm succeeds, a
separate job publishes the matching `server.json` to the MCP Registry using GitHub OIDC; no
registry secret is required. Drafts and prereleases do not publish. If only the registry job
fails, re-run failed jobs to retry it without republishing npm.
39 changes: 34 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,45 @@ attachments and wait for replies, through a deployed
[fmsg Web API](https://github.com/markmnl/fmsg-webapi). Connect through stdio in hosts such as
Claude Code, Claude Desktop, Cursor and VS Code, or through HTTP using API-key headers or configured OAuth.

Use your choice of fmsg hosting provider, or self-host. No hosting provider or identity provider
is built in.

- **stdio** for local hosts: one address per server process, configured by two environment variables.
- **Streamable HTTP** for shared or remote deployments: one endpoint serving many users, each
authenticated by their own fmsg API key or OAuth connection.
- The fmsg Web API client is exported for reuse: `import { FmsgClient } from "@markmnl/fmsg-mcp/client"`.

## 1. Get an fmsg address and API key
## 1. Choose a connection

### OAuth: connect and sign in

Connecting to an existing OAuth-enabled endpoint? Add its MCP URL to your host and sign in;
you can skip the API-key setup below. Operators can enable this with [HTTP OAuth](docs/oauth.md).
Get an OAuth-enabled MCP URL from your fmsg hosting provider, add it to your AI host, and sign in.
You do not need an API key or a local installation for this connection. Your AI host must support
the authorization server's client registration method; see [OAuth onboarding](docs/oauth.md#discovery-and-client-onboarding).
Operators can enable this with [HTTP OAuth](docs/oauth.md).

### API key: use locally or over HTTP

You send as an fmsg address, authenticated by an API key (`fmsgk_…`) issued by your fmsg host:

- **No host yet?** Create an account at a public fmsg host such as [fmsg.io](https://fmsg.io) and
add an agent (sub-account) to get an API URL and key.
- **Using a hosting provider?** Create an account with an fmsg hosting provider and obtain an
API URL and API key for the address your agent will use.
- **Self-hosting?** Run the stack with [fmsg-docker](https://github.com/markmnl/fmsg-docker) and issue
a key with `fmsg-webapi api-key create`.

Use the API URL and key for local installation below, or use your provider's API-key MCP endpoint
with an `Authorization: Bearer fmsgk_...` header. The endpoint must use the fmsg Web API that accepts
your key.

### MCP Registry

Find `io.github.markmnl/fmsg-mcp` in the [official MCP Registry](https://registry.modelcontextprotocol.io/?q=io.github.markmnl%2Ffmsg-mcp).
The listing offers local npm installation and configurable remote connections for OAuth or API keys.
For a remote connection, supply the MCP endpoint from your fmsg hosting provider or your own deployment;
the registry input takes its hostname and path without the `https://` prefix, such as `mcp.example.com/mcp`.
OAuth sign-in is discovered from that endpoint; the API-key option additionally asks for your key.
If your AI host cannot configure registry URL templates, add the full MCP URL directly instead.

## 2. Install

Requires Node.js 22 or later.
Expand Down Expand Up @@ -213,4 +235,11 @@ bash .github/scripts/run-fmsg-docker-e2e.sh # end to end on
See [AGENTS.md](./AGENTS.md) for layout and conventions, [ROADMAP.md](./ROADMAP.md) for remaining
integration work, and [CHANGELOG.md](./CHANGELOG.md) for release notes.

### Releasing

Publish a stable GitHub release tagged `vX.Y.Z` to run the checks, publish the npm package, and
then update the MCP Registry with the same version. Both use OIDC authentication; no dedicated
registry secret is needed. Drafts and prereleases do not publish. Registry publishing runs as a
separate job, so if it fails after npm succeeds, use **Re-run failed jobs** on the release workflow.

[MIT licensed](./LICENSE)
49 changes: 47 additions & 2 deletions server.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "io.github.markmnl/fmsg-mcp",
"description": "Send and receive fmsg federated messages from any AI agent via a deployed fmsg Web API",
"title": "fmsg",
"description": "Federated messaging with your fmsg hosting provider. Connect with OAuth or an API key.",
"websiteUrl": "https://github.com/markmnl/fmsg-mcp#readme",
"repository": {
"url": "https://github.com/markmnl/fmsg-mcp",
"source": "github"
Expand All @@ -19,7 +21,7 @@
"environmentVariables": [
{
"name": "FMSG_API_URL",
"description": "Base URL of the fmsg Web API",
"description": "Base URL of your hosting provider's or self-hosted fmsg Web API",
"isRequired": true
},
{
Expand All @@ -40,5 +42,48 @@
}
]
}
],
"remotes": [
{
"type": "streamable-http",
"url": "https://{oauth_mcp_endpoint}",
"variables": {
"oauth_mcp_endpoint": {
"description": "OAuth MCP endpoint from your fmsg hosting provider: hostname and path without https://. Sign in through your AI host; no API key needed.",
"isRequired": true,
"format": "string",
"placeholder": "mcp.example.com/mcp"
}
}
},
{
"type": "streamable-http",
"url": "https://{api_key_mcp_endpoint}",
"variables": {
"api_key_mcp_endpoint": {
"description": "API-key MCP endpoint from your fmsg hosting provider: hostname and path without https://. Use an endpoint that accepts your key.",
"isRequired": true,
"format": "string",
"placeholder": "mcp.example.com/mcp"
}
},
"headers": [
{
"name": "Authorization",
"description": "Your fmsg API key for the selected MCP endpoint",
"isRequired": true,
"isSecret": true,
"value": "Bearer {api_key}",
"variables": {
"api_key": {
"description": "API key accepted by this endpoint's fmsg Web API, starting with fmsgk_",
"isRequired": true,
"isSecret": true,
"format": "string"
}
}
}
]
}
]
}
Loading