Skip to main content
Glama
lonniev

eXcalibur MCP

by lonniev

eXcalibur MCP

Sword-swift posting of pretty tweets to X (Twitter) via AI agents, monetized with Bitcoin Lightning micropayments through the DPYC(TM) Tollbooth protocol.

Python License

eXcalibur is a FastMCP server that lets AI agents post to X (Twitter) with rich Unicode formatting and optional images. Patron authentication for X uses OAuth2 Authorization Code + PKCE -- no credentials appear in chat. Operator credentials (X app keys, BTCPay) arrive via human-in-the-loop Secure Courier and are stored in the operator's per-operator Neon vault schema. Tool calls are metered with Tollbooth DPYC(TM) pre-funded Lightning balances -- Don't Pester Your Customer.

Part of the DPYC(TM) Social Contract.

Getting Started

Connect via Horizon -- no local install needed:

https://excalibur-mcp.fastmcp.app/mcp

The web editor at https://excalibur.tollbooth-dpyc.com drives the same tools from a browser.

Step 1: Check Session

Call excalibur_session_status to see your current session state. If you have an active session with funded credits, you are ready to post.

Step 2: Prove Your Npub on the Connection

Identity rides on the connection, not on tool arguments. Connecting from an AI assistant is an OAuth holder connection: call excalibur_request_npub_proof(patron_npub=<npub>), reply to the Nostr DM it sends, then call excalibur_receive_npub_proof(patron_npub=<npub>, nonce=<nonce>) once. The connection then holds your npub for the lifetime you chose in the reply (up to 30 days), and every tool call passes only npub. A caller that holds the nsec may instead sign each HTTP request (NIP-98).

Step 3: X OAuth2 + PKCE (Patron Credentials for X)

  1. Get your Nostr npub -- use the dpyc-oracle's how_to_join() tool, or any Nostr client.

  2. Call excalibur_begin_oauth(npub=<npub>) -- returns an authorize_url. Open the primary authorize_url (not a shortlink) in your browser and authorize the app on X.

  3. Call excalibur_check_oauth_status(npub=<npub>) -- completes the code exchange, vaults the tokens, and activates your session.

Sessions are keyed by npub. Tokens are stored in the Neon vault and auto-refresh on expiry.

Step 4: Secure Courier (Operator Credentials)

Operator credentials (X OAuth2 app keys, BTCPay connection) are delivered via Secure Courier (service="excalibur-operator"). This is a human-in-the-loop flow: the operator consciously approves each credential delivery via their Nostr client. On receive_credentials, the relay DM is destructively drained -- credentials exist only in the Neon vault after receipt.

Related MCP server: X-MCP 2.0

Credits Model

Tool calls cost api_sats per call. Each purchase creates a tranche; it expires only if the operator's pricing model sets a tranche_lifetime. Auth and balance tools are always free. Use excalibur_check_balance to see your balance and tranche expiry. Top up via excalibur_purchase_credits.

Available Tools

Standard Tools (from the wheel)

Standard DPYC(TM) tools are registered by register_standard_tools() from tollbooth-dpyc. These include session management, credit operations, Secure Courier, pricing, notarization, and Oracle delegation. Each tool is identified by a deterministic UUID v5.

Category

Tools

Session & Billing

session_status, check_balance, account_statement, account_statement_infographic, restore_credits, service_status, check_price, check_authority_balance

Secure Courier

request_credential_channel, receive_credentials, forget_credentials, update_operator_credential, delete_operator_credential, update_patron_credential, delete_patron_credential, get_patron_credential_fields

Npub Proof

request_npub_proof, receive_npub_proof, check_proof_status, held_identities

Purchase & Coupons

purchase_credits, check_payment, mint_coupon, list_coupons, update_coupon, delete_coupon, redeem_coupon, list_my_coupons, forget_coupon

Pricing & Constraints

get_pricing_model, set_pricing_model, reset_pricing_model, list_constraint_types, list_canonical_identities

Notarization

notarize_ledger, list_notarizations, get_notarization_proof

Onboarding & Adoption

get_operator_onboarding_status, get_patron_onboarding_status, request_adoption, adoption_status, restore_neon_schema

Nostr & Diagnostics

get_nostr_profile, publish_nostr_profile, request_echo, report_issue

OAuth (X)

begin_oauth, check_oauth_status

Oracle (delegated)

oracle_about, oracle_how_to_join, oracle_lookup_member, oracle_get_tax_rate, oracle_network_advisory

Domain Tools (eXcalibur-specific)

Domain tools are defined in server.py (with storage handlers under tools/). Prices are set by the operator's pricing model -- preview any call with check_price. Read and auth tools are free.

Posting to X

Tool

Description

post_tweet

Post a text tweet with markdown-to-Unicode rich text formatting

post_tweet_image

Post a tweet with a hero banner image (image URL or SVG rendered to PNG)

get_x_profile

Fetch the connected X account's handle and name for this patron (free)

post_nostr_message

Publish a public Nostr note (kind 1) for your proven npub, signed by an ephemeral scribe key that p-tags you

Stored posts (drafts & scheduling)

Tool

Description

create_post

Store a new post (draft or scheduled); returns its post_id

get_post

Read one stored post by id (owner-scoped)

list_posts

List your stored posts, server-side sorted, filtered, and paginated

update_post

Patch a stored post (doc, publish_at, recurrence, status)

delete_post

Delete a stored post (soft delete by default)

Snippets & Voice

Tool

Description

list_snippets

List your saved post snippets (server-side sorted/filtered/paginated)

get_snippet

Read one saved snippet by id

save_snippet

Save a reusable snippet (opening/footer/CTA)

delete_snippet

Delete a saved snippet (free, owner-scoped)

get_voice

Read your saved writing Voice (profile blurb + banned words)

save_voice

Save your per-npub writing Voice

AI editorial & dynamic blocks

Tool

Description

refine_post_region

Refine a flagged region of a post with an LLM, server-side

resolve_dynamic_block

Start resolving a dynamic (prompt-backed) post block; returns a claim check

fetch_dynamic_block

Redeem a resolve_dynamic_block claim check (free, proof-gated)

Conversation leads

Tool

Description

find_conversations

Search X for people asking questions you could answer; score and store them as leads

list_conversations

Page through your stored leads (free)

reply_to_conversation

Post your reply into a lead's thread on X and mark the lead engaged

set_conversation_status

Mark a lead new, seen, engaged or dismissed (free)

list_conversation_queries

List your saved conversation queries (free)

save_conversation_query

Create or update a saved conversation query (free)

delete_conversation_query

Delete a saved query; its leads are kept (free)

Post metrics

Tool

Description

harvest_metrics

Cadence-aware sweep that snapshots X metrics for sent posts (operator-only)

get_post_metrics

Read the metrics snapshot series for one of your posts

post_performance

Derived reach scores across your harvested post metrics

post_performance_infographic

SVG infographic of post performance

Scheduler

Tool

Description

process_scheduled_posts

Publish every due scheduled post (operator-only)

get_scheduler_log

Read recent scheduler-tick outcomes

scheduler_status

The scheduler's configuration and current status (free; any proven patron)

scheduler_pending

What the scheduled-post cron Worker is waiting on (operator-only)

scheduler_check_now

Run one scheduler tick now (any proven patron)

scheduler_reissue

Send the scheduler's proof request again, now (operator-only)

OAuth flow tools (begin_oauth, check_oauth_status) are now standard tools provided by the wheel (see the Standard Tools table above).

Tools that take an npub take only the npub: the connection proves it (see Security).

Security

  • Npub identity -- Patrons are identified by a Nostr public key (npub), not an email or password. One keypair per role, managed by the user.

  • Identity on the connection -- A connection proves its npub in one of two ways: each HTTP request is signed with the npub's key (NIP-98, kind 27235), or an OAuth holder connection carries an Operator-signed grant earned through request_npub_proof / receive_npub_proof. The patron chooses the grant's lifetime, up to 30 days; held_identities and check_proof_status show what a connection holds.

  • Human-in-the-loop Secure Courier -- Credential delivery requires conscious operator approval via Nostr DM. On receipt, the relay message is destructively drained so secrets exist only in the encrypted Neon vault.

  • Per-operator Neon schema -- Each operator's credentials are stored in an isolated Postgres schema with a dedicated LOGIN role. No cross-operator access.

Architecture

src/excalibur_mcp/
  server.py        FastMCP server -- domain tools + register_standard_tools()
  config.py        Pydantic settings from environment variables
  oauth_flow.py    X-specific OAuth2 Authorization Code + PKCE wrapper
  x_client.py      X API v2 client with OAuth 2.0 Bearer token auth
  formatter.py     Markdown -> Unicode rich text (bold, italic, headers)
  refine.py        Server-side editorial refinement for the post editor
  resolve.py       Server-side resolution of dynamic (prompt-backed) post blocks
  resolver.py      Builds one post's finished body (the slow half: dynamic blocks)
  publisher.py     Carries a finished body to X (the fast half: bill, post, record)
  scheduler.py     Scheduled-post firing (publishes due posts on the owner's behalf)
  conversation_scoring.py  Pure scoring + query composition for conversation leads
  metrics_harvest.py       Post-metrics harvest cadence and derived reach scores
  nostr_note.py    Public Nostr notes signed by an ephemeral scribe key
  tools/           Domain storage handlers (posts, snippets, voices, conversations, metrics)
  db/              eXcalibur persistence on the wheel's NeonVault (posts, snippets, voices,
                   conversations, metrics, scheduler runs, migrations)
modal_app.py       Detached runner for long dynamic-block resolves (Modal)
scheduler-worker/  Cloudflare Worker cron that fires due scheduled posts
frontend/          The web editor (excalibur.tollbooth-dpyc.com)

Key design choices:

  • OAuth2 Authorization Code + PKCE -- Patron tokens acquired via browser flow. begin_oauth returns the primary authorize_url (not a shortlink); check_oauth_status completes the exchange. Tokens stored in Neon vault, auto-refreshed on expiry. No OAuth 1.0a.

  • UUID v5 tool identity -- Every tool (standard and domain) has a deterministic UUID derived from its capability name. Pricing, constraints, and billing all key on UUID.

  • Standard tools from the wheel -- register_standard_tools() provides all DPYC(TM) infrastructure tools. Only domain-specific X/Twitter tools are defined in server.py.

  • Markdown to Unicode -- Converts **bold**, *italic*, and # headers to Unicode characters that render in tweets without markup syntax.

Self-Hosting

Environment Variables

DPYC(TM) Identity (required to boot)

Variable

Required

Description

TOLLBOOTH_NOSTR_OPERATOR_NSEC

Yes

Operator's Nostr secret key for identity bootstrap and DM encryption

This is the only env var required to start. All other secrets (X app keys, BTCPay credentials) arrive via Secure Courier credential templates and are stored in the per-operator Neon vault schema.

Operator Credentials (via Secure Courier)

These are delivered via Secure Courier (service="excalibur-operator"), not set as environment variables:

Credential

Description

client_id

X OAuth2 Client ID (from X Developer Portal)

client_secret

X OAuth2 Client Secret (from X Developer Portal)

btcpay_host

BTCPay Server URL

btcpay_store_id

Store ID for invoices

btcpay_api_key

API key with invoice permissions

llm_api_key

Optional. Model-router key (OpenRouter by default) for refine_post_region and dynamic blocks; posting works without it

modal_token_id, modal_token_secret, modal_app_name

Optional. Modal workspace that runs long dynamic-block resolves detached from the MCP host

github_repo, github_token

Optional. Where report_issue files patron field reports (token needs only issues:write)

Certified operators bootstrap their Neon database URL from the Authority via encrypted Nostr DM -- NEON_DATABASE_URL is not read from the environment.

Optional Tuning

Variable

Description

SCHEDULER_WORKER_URL

Public URL of the scheduled-post cron Worker (default: https://excalibur-scheduler.lonniev.workers.dev)

RESOLVE_BLOCK_BUDGET_MAX_S

Ceiling, in seconds, on one dynamic block's LLM run (default: 1800); the job, lease and runner timeouts are derived from it

Troubleshooting

Cold start

On first request after deploy, the service may take a few seconds to initialize Neon connections and load the pricing model. The runtime handles this with inline retry -- the caller receives the result after the warm-up completes, not a "service unavailable" error.

Common tool call situations

Responses describe the current lifecycle state with guidance on the next step, not bare error messages.

  • proof_required -- The connection does not prove that npub (or its grant expired). Call request_npub_proof, reply to the DM, then receive_npub_proof once. check_proof_status shows how long a grant has left.

  • "Insufficient credit balance" -- Call purchase_credits to top up. Check tranche expiry with check_balance.

  • "Operator credentials not configured" -- Operator setup issue, not the patron's problem.

Don't Pester Your Customer: Do NOT ask the patron to re-authenticate, re-authorize, or re-do OAuth unless the response specifically says the upstream token is expired or invalid.

Development

# Install in development mode
cd excalibur-mcp
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

# Run tests
.venv/bin/pytest tests/

# Type checking
mypy src/excalibur_mcp/

# Formatting
black src/ tests/
ruff check src/ tests/

DPYC(TM) Ecosystem

Trademarks

DPYC, Tollbooth DPYC, and Don't Pester Your Customer are trademarks of Lonnie VanZandt. See the TRADEMARKS.md in the dpyc-community repository for usage guidelines.

License

Apache License 2.0 -- see LICENSE for details.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI assistants to interact with X (Twitter) API v2 for posting tweets, searching, liking, retweeting, and more through natural language.
    52 PyPI
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    Enables users to interact with X/Twitter via Claude, allowing posting tweets, searching, liking, retweeting, and more using OAuth 2.0 PKCE authentication.
    10
    7
    MIT
  • F
    license
    A
    quality
    D
    maintenance
    Enables AI assistants to post, search, and manage tweets on X (Twitter) via the MCP protocol.
    10
    2
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to fully automate X/Twitter interactions including searching, posting, engaging, and managing multiple accounts without paid API keys.
    MIT