I think you could use unmanaged packages as containers to solve this. First, a distinction: I'm not referring to using unmanaged packages to distribute the code between orgs. What I mean is you can create multiple unmanaged packages in the source org, add metadata to them, and then retrieve from the packages via the metadata api.
The nice thing about packages is that they automatically include any metadata required by metadata you've included in the package. There are a few exceptions such as test classes which have to be manually selected.
To test it out, create an unmanaged package in the org (Setup -> Create -> Package) and add some metadata to it. Any metadata referenced by the metadata you include will be added to the package. You can create multiple packages and add metadata from different functions to them.
Then, retrieve from the metadata api with an ant call like:
<sf:retrieve
username="${sf.username}"
password="${sf.password}"
serverurl="${sf.serverurl}"
retrieveTarget="PackageNameHere"
packageNames="PackageNameHere"/>
You'll get all the metadata and dependencies in a directory with a package.xml file designed to deploy the code into an unmanaged package in another org. Then create the package in the target org manually (no api for this step) with the same name as from the source and then run a normal deploy from the retrieveTarget. It's not distributing via an unmanaged package, it's just using them as a container for metadata.
The way it deploys into the unmanaged package in the target org is by including the following line in the package.xml:
<fullName>YourPackageHere</fullName>
In the target org, the unmanaged packages will contain all the metadata you deployed without needing to manually select it.
The other nice thing about this approach is that the retrieved metadata only includes whatever was in the package. This means if the package has 2 fields on Contact, your objects/Contact.object file will only have those two fields and nothing else.