Skip to content

feat: add mcp_server() helper with built-in server info and credential resolution#9

Merged
Aaron ("AJ") Steers (aaronsteers) merged 19 commits intomainfrom
devin/1768688145-mcp-server-helper
Jan 19, 2026
Merged

feat: add mcp_server() helper with built-in server info and credential resolution#9
Aaron ("AJ") Steers (aaronsteers) merged 19 commits intomainfrom
devin/1768688145-mcp-server-helper

Conversation

@aaronsteers
Copy link
Contributor

@aaronsteers Aaron ("AJ") Steers (aaronsteers) commented Jan 17, 2026

Summary

Adds a new mcp_server() factory function that creates FastMCP instances with common patterns built-in:

  • Automatic server info resource - Registers a {name}://server/info resource with package version, git SHA, FastMCP version, and custom advertised properties
  • HTTP header credential resolution - MCPServerConfigArg allows defining credentials that resolve from HTTP headers first, then fall back to environment variables, then to default values
  • MCP module auto-discovery - Optional auto_discover_assets parameter discovers sibling non-private modules in the caller's package
  • Passthrough to FastMCP - All additional kwargs are passed to the FastMCP constructor

Example usage:

from fastmcp_extensions import mcp_server, MCPServerConfigArg, get_mcp_config

app = mcp_server(
    name="my-server",
    package_name="my-package",
    advertised_properties={"docs_url": "..."},
    server_config_args=[
        MCPServerConfigArg(
            name="api_key",
            http_header_key="X-API-Key",
            env_var="MY_API_KEY",
            default="fallback-value",  # Can also be a callable
        ),
    ],
)
api_key = get_mcp_config(app, "api_key")

Updates since last revision

Based on PR feedback:

  • Renamed resolve_config to get_mcp_config ("get" is more accurate since values may be cached, and "mcp" prefix makes the purpose clearer)
  • Made get_registered_tools, get_registered_prompts, get_registered_resources private (internal implementation details now that mcp_server() handles registration)
  • Made clear_registrations private (_clear_registrations) - only used for testing
  • Added comprehensive module docstring with usage examples and backtick cross-links to key components
  • Added normalize_fn parameter to MCPServerConfigArg for transforming resolved values
  • Updated get_mcp_config to accept either Context or FastMCP for session-aware resolution
  • Renamed _mcp_server_config to x_mcp_server_config to distinguish from FastMCP's private properties
  • Moved package_name to a top-level parameter
  • Implemented _discover_mcp_module_names() using inspect and pkgutil
  • Made http_header_key and env_var optional/nullable
  • Added default field (string or callable)
  • 29 tests total

Review & Testing Checklist for Human

  • Test _discover_mcp_module_names() with a real package - The implementation uses stack inspection (inspect.currentframe()) which can be fragile. Verify it works correctly when called from a real MCP server package.
  • Test normalize_fn behavior - Verify that returning None from normalize_fn triggers fallback to next source, and that exceptions propagate correctly without leaking sensitive values.
  • Test get_mcp_config with Context - Verify session-aware resolution works when passing a Context object from an MCP tool function.
  • Verify sensitive attribute behavior - MCPServerConfigArg.sensitive is defined but not used for masking. Confirm this is intentional for future use.

Suggested test plan:

  1. Create a simple MCP server package with multiple modules using mcp_server(auto_discover_assets=True)
  2. Verify _discover_mcp_module_names() returns the expected module list
  3. Start the server and query the {name}://server/info resource
  4. Test credential resolution with HTTP headers, env vars, default values, and normalize_fn

Notes

Closes #6

Link to Devin run: https://app.devin.ai/sessions/2df3046f40894f978f822b81ecd774ab
Requested by: Aaron ("AJ") Steers (@aaronsteers)

…l resolution

- Add mcp_server() factory function that creates FastMCP instances with:
  - Automatic server info resource registration (version, git SHA, fastmcp version)
  - HTTP header credential resolution with env var fallback
  - Support for advertised properties (package_name, docs_url, etc.)
  - Optional auto_discover_assets parameter

- Add MCPServerConfigArg dataclass for credential configuration
- Add MCPServerConfig dataclass to store server configuration
- Add resolve_config() function for credential resolution
- Add comprehensive tests for all new functionality
- Update README with documentation for new features

Closes #6

Co-Authored-By: AJ Steers <aj@airbyte.io>
@devin-ai-integration
Copy link
Contributor

Original prompt from AJ Steers
@Devin - <https://github.com/airbytehq/fastmcp-extensions/issues/6>
Thread URL: https://airbytehq-team.slack.com/archives/D089P0UPVT4/p1768687993204549

@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@github-actions
Copy link

🎉 Thanks for opening this pull request!

Your contribution is appreciated. Here are some helpful commands you can use:

Quick Commands

  • /autofix - Auto-format and fix linting issues (ruff format + ruff check --fix)
  • /lock - Update the uv.lock file with latest dependencies

Available Poe Tasks

You can run any of these tasks using the slash command: /poe <task-name>

Core Tasks

  • /poe test - Run all tests
  • /poe test-fast - Run tests with fast exit on first failure
  • /poe lint - Check code style and quality
  • /poe format - Format code with ruff
  • /poe deps - Check for unused and missing dependencies
  • /poe check - Run format check, linting, dependency check, and tests

Quick Fixes

  • /poe fix - Auto-format and fix linting issues
  • /poe clean - Clean up build artifacts and cache

Build & Install

  • /poe build - Build the package
  • /poe install - Install with development dependencies

Other Commands

  • /poe version - Show package version
  • /poe pre-commit - Run pre-commit style checks

The CI will automatically run tests when you push commits. Happy coding! 🚀

@github-actions github-actions bot added the enhancement New feature or request label Jan 17, 2026
Co-Authored-By: AJ Steers <aj@airbyte.io>
devin-ai-integration bot and others added 2 commits January 17, 2026 22:31
… default support

Based on PR feedback:
- Rename 'header' field to 'http_header_key' for clarity
- Make http_header_key and env_var optional (nullable)
- Add 'default' field that can be a string or callable
- Update resolution logic to handle nullable fields and defaults
- Add tests for new default value functionality
- Update README documentation

Co-Authored-By: AJ Steers <aj@airbyte.io>
- Rename _discover_domains_from_modules() to _discover_mcp_module_names()
- Update docstrings to use 'MCP modules' instead of 'domains'
- Update advertised_properties key from 'domains' to 'mcp_modules'

Co-Authored-By: AJ Steers <aj@airbyte.io>
…module discovery

Based on PR feedback:
- Move package_name from advertised_properties to a top-level parameter
- Implement _discover_mcp_module_names() to auto-discover sibling modules
- Update README feature description and examples
- Update API reference documentation

Co-Authored-By: AJ Steers <aj@airbyte.io>
devin-ai-integration bot and others added 4 commits January 17, 2026 22:39
Per PR feedback, use 'x_' prefix to distinguish grafted-in property from
the class's own private properties. This avoids name collisions if FastMCP
adds similar functionality in the future.

Co-Authored-By: AJ Steers <aj@airbyte.io>
This allows transforming resolved values before returning them. Useful for
parsing values like 'Bearer <token>' from Authorization headers.

The normalize_fn receives the raw value and returns the normalized value,
or None if the value should be treated as not found (triggering fallback
to the next resolution source).

Co-Authored-By: AJ Steers <aj@airbyte.io>
…ions

Co-Authored-By: AJ Steers <aj@airbyte.io>
- resolve_config now accepts either a Context object (preferred for MCP tools)
  or a FastMCP app instance directly
- When using Context, the function accesses the app via ctx.fastmcp
- This enables session-aware resolution of HTTP headers
- Added example usage in docstring

Co-Authored-By: AJ Steers <aj@airbyte.io>
@devin-ai-integration devin-ai-integration bot marked this pull request as ready for review January 19, 2026 18:42
devin-ai-integration bot and others added 2 commits January 19, 2026 20:15
Per PR feedback, added comprehensive module docstring with:
- Key components section with backtick cross-links
- Basic usage example
- Credential resolution example with MCPServerConfigArg
- MCP module auto-discovery example
- See Also section linking to main components

Co-Authored-By: AJ Steers <aj@airbyte.io>
Copilot AI review requested due to automatic review settings January 19, 2026 20:16
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Per PR feedback, changed from indented blocks (::) to dedented code
blocks (```py) for better readability in documentation.

Co-Authored-By: AJ Steers <aj@airbyte.io>
Per PR feedback:
- Changed from RST-style underlines to markdown ## headers
- See Also section already uses bulleted markdown list

Co-Authored-By: AJ Steers <aj@airbyte.io>
devin-ai-integration bot and others added 3 commits January 19, 2026 20:22
Per PR feedback, the See Also section is redundant since the components
are already linked in the Key Components section above.

Co-Authored-By: AJ Steers <aj@airbyte.io>
Per user feedback, 'get' is more accurate since the value may already
be resolved/cached, and 'mcp' in the name makes it clearer what we're doing.

Changes:
- Renamed public function resolve_config -> get_mcp_config
- Renamed internal method MCPServerConfig.resolve_config -> get_config
- Updated all test function names and calls
- Updated module docstring examples
- Updated README examples and API reference
- Updated __init__.py exports

Co-Authored-By: AJ Steers <aj@airbyte.io>
devin-ai-integration bot and others added 3 commits January 19, 2026 20:46
Per user feedback, these functions are internal implementation details
now that mcp_server() handles registration automatically.

Changes:
- Deleted get_registered_tools, get_registered_prompts, get_registered_resources
- Renamed clear_registrations to _clear_registrations
- Removed exports from __init__.py
- Updated tests to use private _REGISTERED_* lists directly

Co-Authored-By: AJ Steers <aj@airbyte.io>
@aaronsteers Aaron ("AJ") Steers (aaronsteers) merged commit 27fc753 into main Jan 19, 2026
14 checks passed
@aaronsteers Aaron ("AJ") Steers (aaronsteers) deleted the devin/1768688145-mcp-server-helper branch January 19, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: mcp_app() or mcp_server() helper with built-in server info

2 participants